
Genz thân mến, hôm nay chúng ta sẽ mổ xẻ một khái niệm mà nhiều khi mấy đứa cứ thấy nó xuất hiện mà không hiểu 'thằng cha' này từ đâu chui ra: RuntimeException.
1. RuntimeException là gì? Để làm gì? (Giải mã kiểu Gen Z)
Nghe anh Creyt giảng đây! Tưởng tượng thế này: cuộc đời lập trình của mấy đứa y như đi trên đường vậy. Có những cái đèn đỏ (Checked Exception) mà mấy đứa bắt buộc phải dừng lại, phải khai báo trước là 'ê, tao phải xử lý thằng này nha'. Ví dụ như file không tồn tại, kết nối mạng đứt đoạn... đó là những thứ mình biết trước là có thể xảy ra và phải chuẩn bị tinh thần để xử lý.
Còn RuntimeException á? Nó giống như việc mấy đứa đang đi đường bình thường, tự nhiên vấp cục đá tàng hình vậy! Hay đang lái xe mà thằng cha nào đó tự nhiên băng ngang đường không xi nhan, không báo trước. Nó xảy ra đột ngột, không được báo trước khi biên dịch (compile time), mà chỉ bung bét ra khi chương trình đang chạy (runtime).
Nói thẳng ra, RuntimeException thường là lỗi của lập trình viên! Đúng vậy, lỗi do mình ẩu, mình không lường trước được các trường hợp logic mà đáng lẽ ra mình phải xử lý. Ví dụ: cố gắng truy cập phần tử thứ 100 của một mảng chỉ có 10 phần tử (ArrayIndexOutOfBoundsException), hay chia một số cho 0 (ArithmeticException), hoặc tệ hơn là cố gắng thao tác với một đối tượng null (NullPointerException).
Để làm gì? Nó là cách Java 'mắng vốn' mấy đứa: 'Ê thằng kia! Mày viết code sai logic rồi! Sửa lại đi!'. Nó không bắt mấy đứa phải try-catch ngay từ đầu vì nó nghĩ rằng, nếu code đúng logic thì những lỗi này không bao giờ xảy ra.
2. Code Ví Dụ Minh Hoạ Rõ Ràng
Để mấy đứa dễ hình dung, anh cho vài ví dụ kinh điển của RuntimeException:
Ví dụ 1: NullPointerException – 'Thằng cha' này không tồn tại!
public class RuntimeExceptionDemo {
public static void main(String[] args) {
String tenBan = null;
// Cố gắng gọi phương thức trên một đối tượng null
try {
int doDaiTen = tenBan.length(); // Bùm! NullPointerException
System.out.println("Độ dài tên: " + doDaiTen);
} catch (NullPointerException e) {
System.err.println("Ơ kìa! Tên bạn đang là NULL mà đòi đo độ dài à? Sửa lại đi chứ!\n" + e.getMessage());
// Log lỗi hoặc xử lý khác
}
System.out.println("Chương trình tiếp tục chạy (sau khi xử lý lỗi).");
}
}
Ví dụ 2: ArrayIndexOutOfBoundsException – Vượt quá giới hạn cho phép!
public class ArrayErrorDemo {
public static void main(String[] args) {
int[] soMayMan = {1, 7, 9, 24, 68};
try {
// Cố gắng truy cập phần tử thứ 5 (index 4) rồi phần tử thứ 10 (index 9)
System.out.println("Phần tử thứ 5: " + soMayMan[4]);
System.out.println("Phần tử thứ 10: " + soMayMan[9]); // Bùm! ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println("Mảng có 5 phần tử (0-4) mà đòi truy cập phần tử thứ 10 là sao? Sai rồi!\n" + e.getMessage());
}
}
}
Ví dụ 3: Custom RuntimeException – Tạo lỗi 'tự va' của riêng mình
Đôi khi, mấy đứa muốn tạo ra một lỗi logic đặc thù của ứng dụng mà không muốn người dùng (của thư viện/module của mấy đứa) bị ép phải catch. Lúc đó, Custom RuntimeException là lựa chọn 'chất'!
// Bước 1: Định nghĩa Custom RuntimeException
class KhongDuTienException extends RuntimeException {
public KhongDuTienException(String message) {
super(message);
}
}
// Bước 2: Sử dụng trong logic nghiệp vụ
public class GiaoDichTaiChinh {
private double soDuTaiKhoan;
public GiaoDichTaiChinh(double soDu) {
this.soDuTaiKhoan = soDu;
}
public void rutTien(double soTienCanRut) {
if (soTienCanRut <= 0) {
throw new IllegalArgumentException("Số tiền rút phải lớn hơn 0!");
}
if (soTienCanRut > soDuTaiKhoan) {
// Ném RuntimeException của riêng mình
throw new KhongDuTienException("Số dư hiện tại " + soDuTaiKhoan + " không đủ để rút " + soTienCanRut + " VND.");
}
soDuTaiKhoan -= soTienCanRut;
System.out.println("Rút thành công! Số dư mới: " + soDuTaiKhoan);
}
public static void main(String[] args) {
GiaoDichTaiChinh tk = new GiaoDichTaiChinh(1000000);
try {
tk.rutTien(500000); // OK
tk.rutTien(700000); // Bùm! KhongDuTienException
} catch (KhongDuTienException e) {
System.err.println("Giao dịch thất bại: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.err.println("Lỗi đầu vào: " + e.getMessage());
}
}
}

3. Mẹo (Best Practices) để Ghi Nhớ và Dùng Thực Tế
- Đừng lạm dụng
try-catch(Exception e)chung chung: Mấy đứa đừng có mà lười biếng, thấy lỗi là quất ngaycatch(Exception e)tùm lum. Đó là 'bịt mắt' lỗi chứ không phải xử lý. Hãy chỉcatchnhữngRuntimeExceptioncụ thể mà mấy đứa có thể xử lý một cách có ý nghĩa (ví dụ: thông báo cho người dùng biết lỗi nhập liệu, log lại lỗi để dev sửa). Hầu hết cácRuntimeExceptionnên được để cho chương trình crash (hoặc được xử lý ở tầng cao nhất của ứng dụng) để dev biết mà sửa bug gốc. - Fix gốc rễ vấn đề, đừng chỉ 'bịt lỗ':
RuntimeExceptionlà tiếng chuông cảnh tỉnh rằng code của mấy đứa có lỗi logic hoặc thiếu kiểm tra đầu vào. Thay vì cứcatchrồi bỏ qua, hãy đào tận gốc rễ: kiểm tranulltrước khi dùng, kiểm tra biên mảng, kiểm tra điều kiện chia cho 0, validate input người dùng... Đó mới là cách làm của một dev chuyên nghiệp! - Dùng
RuntimeExceptioncho lỗi lập trình: Nếu lỗi đó là do dev viết code sai, không lường trước được, thì cứ đểRuntimeExceptionbung bét. Nó sẽ giúp mấy đứa phát hiện bug sớm hơn. - Khi nào tạo
Custom RuntimeException? Khi mấy đứa muốn signal một lỗi logic cụ thể của ứng dụng mà không muốn ép người dùng của API/thư viện của mình phảicatch. Ví dụ:InsufficientFundsException(như trên),UserNotFoundException(nếu coi việc không tìm thấy user là lỗi logic của hệ thống chứ không phải lỗi 'mong đợi' từ bên ngoài). RuntimeExceptionlà 'lời nhắc nhở' của compiler: Nó không bắt mấy đứa khai báo vì nó tin rằng, nếu mấy đứa viết code đúng chuẩn, những lỗi này sẽ không bao giờ xảy ra. Hãy sống theo niềm tin đó!
4. Ví dụ Thực Tế Các Ứng Dụng/Website đã Ứng Dụng
Hầu hết mọi ứng dụng, website lớn nhỏ đều 'sống chung' với RuntimeException:
- Hệ thống E-commerce (ví dụ: Shopee, Lazada): Khi người dùng cố gắng thêm một sản phẩm vào giỏ hàng mà sản phẩm đó đã hết hàng hoặc không tồn tại, thay vì báo
Checked Exception(buộc mọi nơi gọi phảicatch), hệ thống có thể ném mộtRuntimeExceptiondạngProductNotFoundExceptionhoặcOutOfStockException(nếu coi đây là lỗi logic nghiệp vụ mà không cần caller phảicatchtường minh) để xử lý ở tầng cao hơn, hoặc log lại để dev kiểm tra. - Ngân hàng (ví dụ: Vietcombank, Techcombank): Trong ví dụ
KhongDuTienExceptionở trên, việc tài khoản không đủ tiền để rút là một lỗi nghiệp vụ quan trọng. Nếu coi đây là một lỗi mà hệ thống cần phải dừng giao dịch và thông báo rõ ràng, và không muốn mọi hàmtransferMoneyphảithrows InsufficientFundsException, thì mộtRuntimeExceptionlà phù hợp. - Frameworks (ví dụ: Spring Boot): Các framework hiện đại như Spring rất hay 'bọc' (wrap) các
Checked ExceptionthànhRuntimeExceptionđể làm cho code API của họ sạch sẽ hơn, ít phảitry-catchlằng nhằng ở mọi nơi. Ví dụ, mộtSQLException(Checked) có thể được Spring chuyển thànhDataAccessException(là mộtRuntimeException) để dev tập trung vào logic nghiệp vụ hơn.
5. Thử Nghiệm Đã Từng và Hướng Dẫn Nên Dùng Cho Case Nào
Thử nghiệm đã từng: Hồi xưa anh Creyt mới vào nghề, cũng như mấy đứa, thấy RuntimeException là hoảng hồn, cứ tưởng là lỗi gì ghê gớm lắm. Cứ cố gắng try-catch mọi thứ. Sau này mới ngộ ra, RuntimeException không phải để catch mà là để sửa! Anh đã từng mất cả ngày trời debug một NullPointerException chỉ vì quên kiểm tra một đối tượng trả về từ database có thể là null.
Nên dùng cho case nào?
- Lỗi lập trình (Programmer Errors): Đây là trường hợp phổ biến nhất.
NullPointerException,IndexOutOfBoundsException,IllegalArgumentException(khi tham số truyền vào không hợp lệ)... Những lỗi này cho thấy có một vấn đề cơ bản trong logic hoặc cách sử dụng API của mấy đứa. Hãy để chúng xảy ra và sửa code! - Lỗi không thể phục hồi (Unrecoverable Errors): Những lỗi mà khi xảy ra thì chương trình không thể tiếp tục hoạt động một cách hợp lý được nữa. Ví dụ: hệ thống không thể khởi tạo một tài nguyên quan trọng, hoặc một service cần thiết không thể kết nối.
- Lỗi nghiệp vụ mà không muốn ép buộc caller xử lý: Như ví dụ
KhongDuTienException. Mấy đứa muốn thông báo lỗi này nhưng không muốn mọi hàm gọirutTienphảithrowsvàcatch. Thay vào đó, nó sẽ được xử lý ở tầng cao hơn (ví dụ: tầng Controller trong ứng dụng web).
Khi nào KHÔNG nên dùng RuntimeException?
- Lỗi có thể phục hồi và dự đoán được: Ví dụ: File không tìm thấy, mất kết nối mạng, database down. Đây là những tình huống bên ngoài hệ thống của mấy đứa, có thể xảy ra và mấy đứa nên cung cấp cách để người dùng hoặc hệ thống phục hồi. Lúc này,
Checked Exceptionlà lựa chọn đúng đắn, nó ép buộc dev phải xử lý.
Nhớ nhé Genz, RuntimeException không phải là 'kẻ thù', nó là 'người bạn' giúp mấy đứa viết code chất lượng hơn bằng cách chỉ ra những lỗ hổng trong logic của mình. Hãy học cách lắng nghe nó!
Thuộc Series: Java – OOP
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!