🛡️ Xử Lý Ngoại Lệ - Try-Catch
Chào mừng đến với bài học về Try-Catch - xử lý lỗi trong Café!
🎯 Món Ăn Hôm Nay
Tưởng tượng bạn đang pha cà phê:
- Hết sữa giữa chừng → Exception xảy ra
- Thay bằng sữa hạt → Xử lý lỗi (catch)
- Dọn dẹp máy pha → finally
- Tiếp tục phục vụ → Không crash
Đó chính là Try-Catch - bắt và xử lý lỗi!
🛡️ Try-Catch Là Gì?
Try-Catch = Thử và bắt lỗi (xử lý exception)
try {
// Code có thể gây lỗi
int gia = Integer.parseInt("abc"); // NumberFormatException
} catch (NumberFormatException e) {
// Xử lý khi có lỗi
System.out.println("❌ Lỗi: Không phải số");
}
☕ Ẩn Dụ Café:
- try = Thử pha món
- Exception = Lỗi xảy ra (hết nguyên liệu, máy hỏng)
- catch = Xử lý lỗi (thay thế, sửa chữa)
- finally = Dọn dẹp (luôn chạy)
- throw = Báo lỗi lên trên
📋 Cấu Trúc Try-Catch
try {
// Code có thể gây exception
} catch (ExceptionType1 e) {
// Xử lý exception loại 1
} catch (ExceptionType2 e) {
// Xử lý exception loại 2
} finally {
// Luôn chạy (dù có lỗi hay không)
}
👨🍳 Câu Chuyện Trong Quán
Tình huống 1: Try-Catch Cơ Bản
public class PhaCaPhe {
public static void phaMon(String soLy) {
try {
int sl = Integer.parseInt(soLy);
System.out.println("☕ Đang pha " + sl + " ly");
if (sl > 10) {
System.out.println("⚠️ Số lượng lớn, cần thời gian");
}
System.out.println("✅ Hoàn thành!");
} catch (NumberFormatException e) {
System.out.println("❌ Lỗi: Số lượng không hợp lệ");
System.out.println(" Nhập: \"" + soLy + "\"");
}
}
public static void main(String[] args) {
phaMon("5"); // ✅ OK
phaMon("abc"); // ❌ Exception
phaMon("12"); // ✅ OK
}
}
Output:
☕ Đang pha 5 ly
✅ Hoàn thành!
❌ Lỗi: Số lượng không hợp lệ
Nhập: "abc"
☕ Đang pha 12 ly
⚠️ Số lượng lớn, cần thời gian
✅ Hoàn thành!
Tình huống 2: Multiple Catch
public class TinhToan {
public static void tinhGia(String[] args) {
try {
int soLuong = Integer.parseInt(args[0]);
int donGia = Integer.parseInt(args[1]);
int tongTien = soLuong * donGia;
int trungBinh = tongTien / soLuong;
System.out.printf("💰 Tổng: %,d VND%n", tongTien);
System.out.printf("📊 Trung bình: %,d VND%n", trungBinh);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("❌ Thiếu tham số (cần 2 số)");
} catch (NumberFormatException e) {
System.out.println("❌ Tham số phải là số");
} catch (ArithmeticException e) {
System.out.println("❌ Lỗi tính toán: " + e.getMessage());
}
}
public static void main(String[] args) {
tinhToan(new String[]{"5", "10000"}); // ✅ OK
tinhToan(new String[]{"5"}); // ❌ Thiếu tham số
tinhToan(new String[]{"abc", "100"}); // ❌ Không phải số
}
}
📝 Công Thức Nấu (Code Examples)
Ví Dụ 1: Finally Block
public class QuanLyKetNoi {
public static void xuLyDonHang(boolean coLoi) {
System.out.println("🔌 Mở kết nối database");
try {
System.out.println("📝 Đang xử lý đơn hàng...");
if (coLoi) {
throw new Exception("Lỗi xử lý");
}
System.out.println("✅ Xử lý thành công");
} catch (Exception e) {
System.out.println("❌ Lỗi: " + e.getMessage());
} finally {
// Luôn chạy
System.out.println("🔒 Đóng kết nối database");
}
System.out.println("🏁 Hoàn tất");
}
public static void main(String[] args) {
System.out.println("=== TRƯỜNG HỢP THÀNH CÔNG ===");
xuLyDonHang(false);
System.out.println("\n=== TRƯỜNG HỢP LỖI ===");
xuLyDonHang(true);
}
}
Output:
=== TRƯỜNG HỢP THÀNH CÔNG ===
🔌 Mở kết nối database
📝 Đang xử lý đơn hàng...
✅ Xử lý thành công
🔒 Đóng kết nối database
🏁 Hoàn tất
=== TRƯỜNG HỢP LỖI ===
🔌 Mở kết nối database
📝 Đang xử lý đơn hàng...
❌ Lỗi: Lỗi xử lý
🔒 Đóng kết nối database
🏁 Hoàn tất
Ví Dụ 2: Try-With-Resources (Java 7+)
import java.io.*;
public class DocFile {
public static void docMenu(String tenFile) {
// Try-with-resources - tự động đóng file
try (BufferedReader br = new BufferedReader(new FileReader(tenFile))) {
System.out.println("📋 ĐỌC MENU:\n");
String dong;
while ((dong = br.readLine()) != null) {
System.out.println(dong);
}
} catch (FileNotFoundException e) {
System.out.println("❌ Không tìm thấy file: " + tenFile);
} catch (IOException e) {
System.out.println("❌ Lỗi đọc file: " + e.getMessage());
}
// BufferedReader tự động đóng (không cần finally)
}
public static void main(String[] args) {
docMenu("menu.txt");
}
}
Ví Dụ 3: Nested Try-Catch
public class XuLyDonHang {
public static void xuLy(String maDon, String soLuong, String gia) {
try {
System.out.println("📝 Xử lý đơn: " + maDon);
try {
int sl = Integer.parseInt(soLuong);
int g = Integer.parseInt(gia);
int tongTien = sl * g;
System.out.printf("💰 Tổng: %,d VND%n", tongTien);
} catch (NumberFormatException e) {
System.out.println("❌ Lỗi số liệu");
throw e; // Ném lại cho catch ngoài
}
System.out.println("✅ Xử lý thành công");
} catch (Exception e) {
System.out.println("❌ Đơn hàng " + maDon + " thất bại");
}
}
public static void main(String[] args) {
xuLy("DH001", "5", "50000");
xuLy("DH002", "abc", "60000");
}
}
Ví Dụ 4: Multi-Catch (Java 7+)
public class MultiCatch {
public static void thaoTac(int loai) {
try {
if (loai == 1) {
int result = 10 / 0; // ArithmeticException
} else if (loai == 2) {
String s = null;
s.length(); // NullPointerException
} else if (loai == 3) {
int[] arr = {1, 2};
int x = arr[5]; // ArrayIndexOutOfBoundsException
}
} catch (ArithmeticException | NullPointerException | ArrayIndexOutOfBoundsException e) {
// Bắt nhiều exception cùng lúc
System.out.println("❌ Lỗi: " + e.getClass().getSimpleName());
System.out.println(" " + e.getMessage());
}
}
public static void main(String[] args) {
thaoTac(1);
thaoTac(2);
thaoTac(3);
}
}
Ví Dụ 5: Getting Exception Info
public class ThongTinException {
public static void chia(int a, int b) {
try {
int ketQua = a / b;
System.out.println("Kết quả: " + ketQua);
} catch (ArithmeticException e) {
System.out.println("❌ EXCEPTION XẢY RA:");
System.out.println(" Message: " + e.getMessage());
System.out.println(" Class: " + e.getClass().getName());
System.out.println(" Cause: " + e.getCause());
System.out.println("\n📋 Stack Trace:");
e.printStackTrace();
}
}
public static void main(String[] args) {
chia(10, 0);
}
}
Ví Dụ 6: Return Trong Try-Catch-Finally
public class ReturnTryCatch {
public static int tinhGia(String soLuong) {
try {
int sl = Integer.parseInt(soLuong);
System.out.println("✅ Tính giá cho " + sl + " ly");
return sl * 50000; // Return trong try
} catch (NumberFormatException e) {
System.out.println("❌ Số lượng không hợp lệ");
return 0; // Return trong catch
} finally {
System.out.println("🧹 Dọn dẹp...");
// finally chạy trước khi return
}
}
public static void main(String[] args) {
int gia1 = tinhGia("5");
System.out.println("Giá: " + gia1 + "\n");
int gia2 = tinhGia("abc");
System.out.println("Giá: " + gia2);
}
}
Output:
✅ Tính giá cho 5 ly
🧹 Dọn dẹp...
Giá: 250000
❌ Số lượng không hợp lệ
🧹 Dọn dẹp...
Giá: 0
🔥 Thực Hành Trong Quán
Bài Tập 1: Kiểm Tra Tuổi
import java.util.Scanner;
public class KiemTraTuoi {
public static void kiemTra() {
Scanner sc = new Scanner(System.in);
try {
System.out.print("Nhập tuổi: ");
String input = sc.nextLine();
int tuoi = Integer.parseInt(input);
if (tuoi < 0 || tuoi > 150) {
throw new IllegalArgumentException("Tuổi không hợp lệ");
}
if (tuoi >= 18) {
System.out.println("✅ Được vào quán");
} else {
System.out.println("❌ Chưa đủ tuổi");
}
} catch (NumberFormatException e) {
System.out.println("❌ Vui lòng nhập số");
} catch (IllegalArgumentException e) {
System.out.println("❌ " + e.getMessage());
} finally {
sc.close();
}
}
public static void main(String[] args) {
kiemTra();
}
}
Bài Tập 2: Chia Array An Toàn
public class ChiaArray {
public static double tinhTrungBinh(int[] numbers) {
try {
if (numbers == null) {
throw new NullPointerException("Mảng null");
}
if (numbers.length == 0) {
throw new IllegalArgumentException("Mảng rỗng");
}
int tong = 0;
for (int num : numbers) {
tong += num;
}
return (double) tong / numbers.length;
} catch (NullPointerException e) {
System.out.println("❌ Lỗi: " + e.getMessage());
return 0;
} catch (IllegalArgumentException e) {
System.out.println("❌ Lỗi: " + e.getMessage());
return 0;
}
}
public static void main(String[] args) {
int[] gia1 = {50000, 60000, 55000};
int[] gia2 = {};
int[] gia3 = null;
System.out.println("TB 1: " + tinhTrungBinh(gia1));
System.out.println("TB 2: " + tinhTrungBinh(gia2));
System.out.println("TB 3: " + tinhTrungBinh(gia3));
}
}
⚠️ Lỗi Thường Gặp
Lỗi 1: Catch Sai Thứ Tự
// ❌ SAI: Exception cha trước con
try {
// code
} catch (Exception e) { // Cha (bắt tất cả)
// ...
} catch (IOException e) { // ❌ Lỗi! Không bao giờ đến đây
// ...
}
// ✅ ĐÚNG: Exception con trước cha
try {
// code
} catch (IOException e) { // Con (cụ thể)
// ...
} catch (Exception e) { // Cha (chung)
// ...
}
Lỗi 2: Empty Catch Block
// ❌ SAI: Bắt rồi bỏ qua
try {
int gia = Integer.parseInt("abc");
} catch (NumberFormatException e) {
// Không làm gì → Im lặng nuốt exception
}
// ✅ ĐÚNG: Xử lý hoặc log
try {
int gia = Integer.parseInt("abc");
} catch (NumberFormatException e) {
System.out.println("Lỗi: " + e.getMessage());
// Hoặc: e.printStackTrace();
}
Lỗi 3: Quá Chung Chung
// ❌ SAI: Bắt Exception chung
try {
// code
} catch (Exception e) { // Quá chung!
// Không biết lỗi gì
}
// ✅ ĐÚNG: Bắt cụ thể
try {
// code
} catch (IOException e) {
// Xử lý lỗi IO
} catch (SQLException e) {
// Xử lý lỗi DB
}
Lỗi 4: Finally Với Return
// ⚠️ CẨN THẬN: Return trong finally
public int test() {
try {
return 1;
} finally {
return 2; // ⚠️ Override return của try!
}
// Luôn return 2
}
💡 Bí Quyết Của Barista
- Try nhỏ gọn: Chỉ bao code có thể lỗi
- Catch cụ thể: Bắt exception rõ ràng
- Thứ tự catch: Con trước, cha sau
- Xử lý đúng: Không empty catch
- Finally dọn dẹp: Đóng resource, giải phóng
- Log exception: Ghi lại để debug
🎓 Bạn Đã Học Được
- ✅ Try-catch = Thử và bắt lỗi
- ✅ try block = Code có thể lỗi
- ✅ catch block = Xử lý exception
- ✅ finally block = Luôn chạy
- ✅ Multiple catch blocks
- ✅ Multi-catch (Java 7+)
- ✅ Try-with-resources
- ✅ Nested try-catch
☕ Món Tiếp Theo
Đã biết try-catch! Giờ học về throw và throws:
👉 Throw & Throws - Ném Ngoại Lệ
💡 Lời Khuyên Cuối: Try-catch như bảo hiểm - chuẩn bị sẵn phương án B khi có sự cố!