Chuyển tới nội dung chính

🛡️ 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

  1. Try nhỏ gọn: Chỉ bao code có thể lỗi
  2. Catch cụ thể: Bắt exception rõ ràng
  3. Thứ tự catch: Con trước, cha sau
  4. Xử lý đúng: Không empty catch
  5. Finally dọn dẹp: Đóng resource, giải phóng
  6. 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ố!