[Java Backend Zero to Hello] BÀI 1.9: XỬ LÝ NGOẠI LỆ (EXCEPTION HANDLING)
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 1: Java Core Cơ Bản | Bài 11/86
BÀI 1.9: XỬ LÝ NGOẠI LỆ (EXCEPTION HANDLING)
Mục tiêu
- Hiểu cơ chế xử lý lỗi trong Java
- Phân biệt Checked và Unchecked Exception
- Sử dụng try-catch-finally, throw, throws
- Tạo Custom Exception
1. EXCEPTION LÀ GÌ?
Exception là sự kiện bất thường xảy ra trong quá trình thực thi, làm gián đoạn luồng chương trình.
Ví dụ
- Chia cho 0
- Truy cập mảng ngoài phạm vi
- Mở file không tồn tại
- Nhập sai định dạng dữ liệu
2. HỆ THỐNG PHÂN CẤP EXCEPTION
Throwable
├── Error (Không xử lý được)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...
└── Exception (Có thể xử lý)
├── IOException (Checked)
├── SQLException (Checked)
├── ClassNotFoundException (Checked)
└── RuntimeException (Unchecked)
├── NullPointerException
├── ArrayIndexOutOfBoundsException
├── ArithmeticException
├── NumberFormatException
└── IllegalArgumentException
Checked vs Unchecked
| Loại | Khi nào | Bắt buộc xử lý? |
|---|---|---|
| Checked | Lỗi dự đoán được (I/O, network) | ✅ Bắt buộc try-catch hoặc throws |
| Unchecked | Lỗi logic (RuntimeException) | ❌ Không bắt buộc |
| Error | Lỗi hệ thống (JVM) | ❌ Không nên xử lý |
3. CÚ PHÁP TRY-CATCH-FINALLY
3.1 Cơ bản
try {
// Code có thể gây exception
int result = 10 / 0;
} catch (ArithmeticException e) {
// Xử lý khi exception xảy ra
System.out.println("Lỗi chia cho 0: " + e.getMessage());
} finally {
// Luôn thực thi (dù có exception hay không)
System.out.println("Kết thúc xử lý");
}
3.2 Nhiều catch block
try {
String s = null;
System.out.println(s.length());
} catch (NullPointerException e) {
System.out.println("Null pointer");
} catch (ArithmeticException e) {
System.out.println("Lỗi toán học");
} catch (Exception e) {
// Catch tổng quát - đặt cuối cùng
System.out.println("Lỗi khác: " + e.getMessage());
}
3.3 Multi-catch (Java 7+)
try {
// ...
} catch (IOException | SQLException e) {
System.out.println("Lỗi I/O hoặc SQL: " + e.getMessage());
}
3.4 Try-with-resources (Java 7+)
Tự động đóng resource (AutoCloseable).
try (Scanner sc = new Scanner(new File("data.txt"));
BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
// Sử dụng sc, br
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// sc và br tự động được đóng
4. THROW VÀ THROWS
4.1 Throw - Ném exception
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Tuổi không được âm");
}
this.age = age;
}
4.2 Throws - Khai báo exception
public void readFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
// ...
}
4.3 So sánh
throw |
throws |
|---|---|
| Ném một exception cụ thể | Khai báo method có thể ném exception |
| Dùng trong thân method | Dùng trong signature method |
| Theo sau là một object | Theo sau là một hoặc nhiều class |
5. CÁC EXCEPTION PHỔ BIẾN
5.1 NullPointerException
String s = null;
System.out.println(s.length()); // ❌ NullPointerException
// Cách tránh
if (s != null) {
System.out.println(s.length());
}
// Hoặc dùng Optional (Java 8+)
Optional.ofNullable(s).ifPresent(str -> System.out.println(str.length()));
5.2 ArrayIndexOutOfBoundsException
int[] arr = {1, 2, 3};
System.out.println(arr[5]); // ❌
// Cách tránh
if (index >= 0 && index < arr.length) {
System.out.println(arr[index]);
}
5.3 NumberFormatException
String s = "abc";
int n = Integer.parseInt(s); // ❌
// Cách tránh
try {
int n = Integer.parseInt(s);
} catch (NumberFormatException e) {
System.out.println("Không phải số");
}
5.4 ArithmeticException
int result = 10 / 0; // ❌
// Cách tránh
if (divisor != 0) {
int result = dividend / divisor;
}
5.5 ClassCastException
Object obj = "Hello";
Integer n = (Integer) obj; // ❌
// Cách tránh
if (obj instanceof Integer) {
Integer n = (Integer) obj;
}
6. CUSTOM EXCEPTION
6.1 Unchecked Custom Exception
public class InsufficientFundsException extends RuntimeException {
private double amount;
public InsufficientFundsException(String message, double amount) {
super(message);
this.amount = amount;
}
public double getAmount() {
return amount;
}
}
// Sử dụng
public class BankAccount {
private double balance;
public void withdraw(double amount) {
if (amount > balance) {
throw new InsufficientFundsException(
"Số dư không đủ. Thiếu: " + (amount - balance),
amount - balance
);
}
balance -= amount;
}
}
6.2 Checked Custom Exception
public class InvalidUserException extends Exception {
public InvalidUserException(String message) {
super(message);
}
public InvalidUserException(String message, Throwable cause) {
super(message, cause);
}
}
// Sử dụng - bắt buộc try-catch hoặc throws
public void register(User user) throws InvalidUserException {
if (user.getAge() < 18) {
throw new InvalidUserException("Người dùng chưa đủ 18 tuổi");
}
// ...
}
7. CÁC METHOD CỦA EXCEPTION
try {
// ...
} catch (Exception e) {
System.out.println(e.getMessage()); // Thông báo lỗi
System.out.println(e.toString()); // Tên + message
e.printStackTrace(); // In stack trace
System.out.println(e.getClass().getName()); // Tên class
}
Stack trace
java.lang.ArithmeticException: / by zero
at com.example.Main.divide(Main.java:10)
at com.example.Main.main(Main.java:5)
8. BEST PRACTICES
8.1 Catch exception cụ thể
// ❌ Xấu
try {
// ...
} catch (Exception e) {
e.printStackTrace();
}
// ✅ Tốt
try {
// ...
} catch (FileNotFoundException e) {
// Xử lý cụ thể
}
8.2 Không nuốt exception
// ❌ Xấu
try {
// ...
} catch (Exception e) {
// Không làm gì
}
// ✅ Tốt
try {
// ...
} catch (Exception e) {
log.error("Lỗi xảy ra", e);
throw new RuntimeException(e);
}
8.3 Dùng try-with-resources
// ❌ Dài dòng
Connection conn = null;
try {
conn = dataSource.getConnection();
// ...
} finally {
if (conn != null) conn.close();
}
// ✅ Gọn
try (Connection conn = dataSource.getConnection()) {
// ...
}
8.4 Không dùng exception cho luồng điều khiển
// ❌ Xấu
try {
while (true) {
int x = iterator.next(); // Ném exception khi hết
}
} catch (NoSuchElementException e) {
// Dùng exception để thoát vòng lặp
}
// ✅ Tốt
while (iterator.hasNext()) {
int x = iterator.next();
}
8.5 Document exception với Javadoc
/**
* Rút tiền từ tài khoản.
*
* @param amount Số tiền cần rút
* @throws InsufficientFundsException Nếu số dư không đủ
*/
public void withdraw(double amount) throws InsufficientFundsException {
// ...
}
9. BÀI TẬP THỰC HÀNH
Bài 1: Máy tính an toàn
public class SafeCalculator {
public static double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("Không thể chia cho 0");
}
return (double) a / b;
}
}
Bài 2: Validate User
Tạo custom exception InvalidEmailException, InvalidPasswordException và validate user.
Bài 3: Đọc file an toàn
public String readFile(String path) {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
return "Không đọc được file: " + e.getMessage();
}
}
Bài 4: Hệ thống đặt vé
SeatUnavailableException- Ghế đã được đặtInvalidPaymentException- Thanh toán không hợp lệ
10. TÓM TẮT
| Khái niệm | Điểm chính |
|---|---|
| Checked Exception | Bắt buộc xử lý (compile time) |
| Unchecked Exception | RuntimeException, không bắt buộc |
| try-catch-finally | Bắt và xử lý exception |
| try-with-resources | Tự động đóng resource |
| throw | Ném exception |
| throws | Khai báo method có thể ném exception |
| Custom Exception | Kế thừa Exception hoặc RuntimeException |
Bài tiếp theo: 1.10 Collections Framework (phần 1)
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 1.8: ABSTRACT CLASS & INTERFACE
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: BÀI 1.10: COLLECTIONS FRAMEWORK (PHẦN 1)
All rights reserved