1. Làm quen với ngoại lệ
Hãy bắt đầu với một phép so sánh đơn giản. Hãy tưởng tượng chương trình của bạn giống như một chiếc ô tô đang chạy trên đường. Mọi thứ đều ổn cho đến khi xảy ra điều gì đó bất ngờ: xịt lốp, hết xăng, động cơ quá nhiệt. Nếu không “xử lý” tình huống này (ví dụ không thay lốp dự phòng hoặc không đổ xăng), chiếc xe sẽ dừng lại và không thể tiếp tục. Trong chương trình cũng vậy: nếu không xử lý ngoại lệ, chương trình sẽ kết thúc đột ngột.
Ngoại lệ (exception) là một đối tượng được tạo ra khi có vấn đề phát sinh trong lúc chương trình đang chạy. Ngoại lệ như đang nói: “Dừng lại! Có gì đó không ổn!”
Ví dụ về các tình huống gây ra ngoại lệ:
- Chia cho 0 (10 / 0)
- Cố gắng truy cập phần tử mảng không tồn tại (arr[999])
- Mở một tệp không tồn tại trên đĩa
- Chuyển chuỗi "abc" thành số (Integer.parseInt("abc"))
Quan trọng: Ngoại lệ không phải là lỗi biên dịch (ví dụ: lỗi gõ trong mã), mà là các vấn đề phát sinh trong khi chương trình đang chạy.
Tại sao ngoại lệ lại tốt?
- Mã sạch: Logic chính của chương trình không bị lộn xộn bởi những kiểm tra vụn vặt ở khắp nơi.
- Linh hoạt: Có thể xử lý lỗi một cách tập trung (ví dụ, gom mọi lỗi đọc tệp về một nơi).
- Độ tin cậy: Chương trình không “sập” với một thông báo khó hiểu, mà cho biết chính xác điều gì đã xảy ra.
2. Ngoại lệ (Exceptions) vs Lỗi (Errors)
Trong Java có hai loại vấn đề chính có thể phát sinh trong khi chương trình chạy:
Lỗi (Errors)
Lỗi là các vấn đề nghiêm trọng, không thể khôi phục, xuất phát từ sự cố của chính máy ảo Java (JVM). Thông thường chúng liên quan đến tài nguyên của máy tính hoặc lỗi nội bộ của JVM.
Ví dụ về lỗi:
- OutOfMemoryError — hết bộ nhớ.
- StackOverflowError — tràn stack (ví dụ do đệ quy vô hạn).
Quan trọng: Thông thường không nên cố gắng xử lý những lỗi như vậy trong chương trình.
Ngoại lệ (Exceptions)
Ngoại lệ là những vấn đề mà chương trình có thể (và nên) xử lý. Chúng xuất hiện do lỗi trong logic chương trình hoặc do yếu tố bên ngoài (ví dụ: người dùng nhập không đúng như mong đợi).
Ví dụ về ngoại lệ:
- NullPointerException — cố gắng dùng null như một đối tượng.
- ArrayIndexOutOfBoundsException — vượt quá giới hạn mảng.
- IOException — lỗi khi làm việc với tệp.
Ngoại lệ trong Java có hai loại:
- Checked exceptions (có kiểm tra) — trình biên dịch yêu cầu bạn xử lý (ví dụ, IOException).
- Unchecked exceptions (không kiểm tra) — trình biên dịch không yêu cầu xử lý (ví dụ, NullPointerException).
Sơ đồ: vị trí các loại
graph TD
Throwable --> Error
Throwable --> Exception
Exception --> RuntimeException
Exception --> Checked["(các ngoại lệ khác)"]
style Throwable fill:#ffa64d,color:#000
style Exception fill:#ffa64d,color:#000
style Checked fill:#ffa64d,color:#000
style Error fill:#ff4d4d,color:#fff
style RuntimeException fill:#4dff88,color:#000
Giải thích:
- Throwable — gốc của toàn bộ hệ phân cấp lỗi và ngoại lệ trong Java.
- Error — lỗi nghiêm trọng của JVM, không xử lý.
- Exception — các ngoại lệ “bình thường”, mà bạn có thể và nên xử lý.
- RuntimeException — ngoại lệ không kiểm tra (lỗi của lập trình viên).
3. Trông như thế nào trong mã?
Ví dụ 1: Chia cho 0
public class ExceptionDemo
{
public static void main(String[] args)
{
int a = 10;
int b = 0;
int c = a / b; // ArithmeticException sẽ xảy ra tại đây!
System.out.println("Kết quả: " + c);
}
}
Điều gì sẽ xảy ra?
Chương trình sẽ kết thúc với lỗi và bạn sẽ thấy thông báo:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:5)
Ví dụ 2: Truy cập phần tử mảng không tồn tại
int[] arr = {1, 2, 3};
System.out.println(arr[10]); // ArrayIndexOutOfBoundsException
Ví dụ 3: Làm việc với tệp
import java.io.FileReader;
import java.io.IOException;
public class FileDemo
{
public static void main(String[] args) throws IOException
{
FileReader reader = new FileReader("nofile.txt"); // FileNotFoundException (checked)
int data = reader.read();
System.out.println(data);
reader.close();
}
}
Và bây giờ chúng ta sẽ cùng tìm hiểu cách xử lý gọn gàng những tình huống ngoại lệ như vậy.
GO TO FULL VERSION