⚠️ Ném Ngoại Lệ - throw & throws
🎯 Món Ăn Hôm Nay
Trong quán café, không phải lúc nào cũng có thể xử lý sự cố ngay tại chỗ. Đôi khi bạn cần báo cáo lên cấp trên hoặc chủ động tạo ra cảnh báo khi phát hiện vấn đề. Hôm nay chúng ta học cách ném ngoại lệ (throw) và khai báo ngoại lệ (throws) trong Java!
☕ throw & throws Là Gì?
throw và throws là hai từ khóa giúp quán café xử lý các tình huống đặc biệt:
throw: Chủ động ném một ngoại lệ khi phát hiện vấn đề (như barista la lên "Hết sữa!")throws: Khai báo rằng một phương thức có thể ném ngoại lệ (như biển "Có thể hết đồ" treo ở quầy)
Tưởng tượng:
throw= Barista chủ động báo cáo vấn đề ngay khi phát hiệnthrows= Biển báo cảnh báo trước rằng có thể xảy ra vấn đề
👨🍳 Câu Chuyện Trong Quán
Tình huống 1: Barista phát hiện hết sữa khi đang pha Latte
Thay vì cố pha tiếp, barista chủ động báo cáo (throw):
if (soLuongSua == 0) {
throw new Exception("❌ Hết sữa! Không thể pha Latte.");
}
Tình huống 2: Quán treo biển cảnh báo
Quán treo biển "Có thể hết nguyên liệu vào giờ cao điểm" (throws):
public void phaLatte() throws Exception {
// Code pha Latte - có thể ném ngoại lệ
}
📝 Công Thức Ném Ngoại Lệ
Ví Dụ 1: throw - Ném Ngoại Lệ Chủ Động
Kiểm tra số lượng trước khi pha:
public class QuanCafe {
public static void phaLatte(int soLuongSua) {
// ☕ Kiểm tra nguyên liệu trước khi pha
if (soLuongSua <= 0) {
throw new IllegalArgumentException("❌ Hết sữa! Cần ít nhất 200ml");
}
// ✅ Pha bình thường nếu đủ nguyên liệu
System.out.println("☕ Đang pha Latte...");
System.out.println("✅ Latte đã sẵn sàng!");
}
public static void main(String[] args) {
try {
phaLatte(0); // ❌ Sẽ ném ngoại lệ
} catch (IllegalArgumentException e) {
System.out.println("Lỗi: " + e.getMessage());
}
}
}
Output:
Lỗi: ❌ Hết sữa! Cần ít nhất 200ml
Ví Dụ 2: throws - Khai Báo Ngoại Lệ
Phương thức có thể ném ngoại lệ:
public class QuanCafe {
// 📋 Khai báo rằng phương thức này có thể ném Exception
public static void phaDoUong(String loai) throws Exception {
if (loai.equals("Latte")) {
throw new Exception("❌ Máy pha espresso đang bảo trì!");
}
System.out.println("☕ Pha " + loai + " thành công!");
}
public static void main(String[] args) {
try {
phaDoUong("Latte");
} catch (Exception e) {
System.out.println("Lỗi: " + e.getMessage());
System.out.println("💡 Đề xuất: Thử Americano thay thế");
}
}
}
Output:
Lỗi: ❌ Máy pha espresso đang bảo trì!
💡 Đề xuất: Thử Americano thay thế
Ví Dụ 3: Ném Nhiều Loại Ngoại Lệ
Kiểm tra nhiều điều kiện:
public class QuanCafe {
public static void tinhTien(int soLuong, int donGia) {
// Kiểm tra số lượng
if (soLuong <= 0) {
throw new IllegalArgumentException("❌ Số lượng phải > 0");
}
// Kiểm tra đơn giá
if (donGia < 10000) {
throw new IllegalArgumentException("❌ Đơn giá tối thiểu 10,000 VND");
}
// Tính tiền
int tongTien = soLuong * donGia;
System.out.printf("✅ Tổng tiền: %,d VND%n", tongTien);
}
public static void main(String[] args) {
try {
tinhTien(3, 50000); // ✅ OK
tinhTien(-1, 50000); // ❌ Lỗi số lượng
} catch (IllegalArgumentException e) {
System.out.println("Lỗi: " + e.getMessage());
}
}
}
Output:
✅ Tổng tiền: 150,000 VND
Lỗi: ❌ Số lượng phải > 0
Ví Dụ 4: throws Với Nhiều Ngoại Lệ
Khai báo nhiều loại ngoại lệ:
import java.io.*;
public class QuanCafe {
// Có thể ném IOException hoặc InterruptedException
public static void docMenu(String file)
throws IOException, InterruptedException {
// Đọc file menu
BufferedReader reader = new BufferedReader(new FileReader(file));
System.out.println("📋 Đang đọc menu...");
// Giả lập loading
Thread.sleep(1000);
String line;
while ((line = reader.readLine()) != null) {
System.out.println("☕ " + line);
}
reader.close();
}
public static void main(String[] args) {
try {
docMenu("menu.txt");
} catch (FileNotFoundException e) {
System.out.println("❌ Không tìm thấy file menu!");
} catch (IOException e) {
System.out.println("❌ Lỗi đọc file: " + e.getMessage());
} catch (InterruptedException e) {
System.out.println("❌ Bị gián đoạn!");
}
}
}
Ví Dụ 5: Re-throw - Ném Lại Ngoại Lệ
Xử lý một phần rồi ném lại:
public class QuanCafe {
public static void phaLatte(int soLuongSua) throws Exception {
try {
if (soLuongSua <= 0) {
throw new Exception("Hết sữa");
}
System.out.println("☕ Pha Latte thành công!");
} catch (Exception e) {
// Ghi log lỗi
System.out.println("📝 Ghi nhận: " + e.getMessage());
// Ném lại ngoại lệ cho cấp trên xử lý
throw new Exception("❌ Không thể phục vụ Latte: " + e.getMessage());
}
}
public static void main(String[] args) {
try {
phaLatte(0);
} catch (Exception e) {
System.out.println("🔔 Thông báo khách: " + e.getMessage());
}
}
}
Output:
📝 Ghi nhận: Hết sữa
🔔 Thông báo khách: ❌ Không thể phục vụ Latte: Hết sữa
Ví Dụ 6: Checked vs Unchecked Exceptions
Sự khác biệt giữa hai loại:
public class QuanCafe {
// Unchecked Exception - Không bắt buộc throws
public static void kiemTraGia(int gia) {
if (gia < 0) {
throw new IllegalArgumentException("❌ Giá không hợp lệ");
}
}
// Checked Exception - BẮT BUỘC throws
public static void docFile(String path) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(path));
reader.close();
}
public static void main(String[] args) {
// Unchecked - không bắt buộc try-catch
kiemTraGia(50000);
// Checked - BẮT BUỘC try-catch
try {
docFile("data.txt");
} catch (IOException e) {
System.out.println("❌ Lỗi đọc file");
}
}
}
🔥 Thực Hành Trong Quán
Bài Tập 1: Kiểm Tra Giờ Mở Cửa
Viết phương thức kiemTraGioMoCua(int gio) ném ngoại lệ nếu ngoài giờ (7h-22h).
public static void kiemTraGioMoCua(int gio) {
if (gio < 7 || gio > 22) {
throw new IllegalArgumentException(
"❌ Quán đóng cửa! Giờ mở cửa: 7h-22h"
);
}
System.out.println("✅ Quán đang mở cửa!");
}
// Test
try {
kiemTraGioMoCua(23);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
Bài Tập 2: Quản Lý Tồn Kho
Viết phương thức xuatKho(String item, int soLuong) throws Exception kiểm tra tồn kho trước khi xuất.
import java.util.HashMap;
public static HashMap<String, Integer> tonKho = new HashMap<>();
public static void xuatKho(String item, int soLuong) throws Exception {
if (!tonKho.containsKey(item)) {
throw new Exception("❌ Không tìm thấy " + item);
}
int hienCo = tonKho.get(item);
if (hienCo < soLuong) {
throw new Exception("❌ Không đủ " + item +
" (Còn: " + hienCo + ")");
}
tonKho.put(item, hienCo - soLuong);
System.out.println("✅ Xuất " + soLuong + " " + item);
}
// Test
tonKho.put("Sữa", 10);
try {
xuatKho("Sữa", 5); // ✅ OK
xuatKho("Sữa", 10); // ❌ Không đủ
} catch (Exception e) {
System.out.println(e.getMessage());
}
Bài Tập 3: Validate Đơn Hàng
Viết phương thức validateDonHang(int soLuong, int gia) ném các ngoại lệ khác nhau.
public static void validateDonHang(int soLuong, int gia) {
if (soLuong <= 0) {
throw new IllegalArgumentException("❌ Số lượng phải > 0");
}
if (soLuong > 100) {
throw new IllegalArgumentException("❌ Tối đa 100 ly/đơn");
}
if (gia < 10000) {
throw new IllegalArgumentException("❌ Giá tối thiểu 10,000 VND");
}
System.out.println("✅ Đơn hàng hợp lệ!");
}
// Test
try {
validateDonHang(5, 50000); // ✅ OK
validateDonHang(0, 50000); // ❌ Số lượng không hợp lệ
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
⚠️ Những Lỗi Đầu Bếp Thường Gặp
Lỗi 1: Quên throws Cho Checked Exception
❌ SAI:
// Lỗi biên dịch - thiếu throws
public void docFile(String path) {
BufferedReader reader = new BufferedReader(new FileReader(path));
}
✅ ĐÚNG:
// Thêm throws IOException
public void docFile(String path) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(path));
reader.close();
}
Lỗi 2: Throw Null
❌ SAI:
throw null; // ❌ NullPointerException!
✅ ĐÚNG:
throw new Exception("Mô tả lỗi cụ thể");
Lỗi 3: Ném Exception Quá Chung
❌ SAI:
// Quá chung chung, khó debug
throw new Exception("Lỗi");
✅ ĐÚNG:
// Cụ thể và có thông tin chi tiết
throw new IllegalArgumentException(
"Số lượng sữa phải >= 200ml, nhận được: " + soLuong + "ml"
);
Lỗi 4: Throws Nhưng Không Throw
❌ SAI:
// Khai báo throws nhưng không bao giờ throw
public void phaLatte() throws Exception {
System.out.println("Pha Latte");
// Không có throw ở đâu cả!
}
✅ ĐÚNG:
// Chỉ throws khi thực sự cần
public void phaLatte(int sua) throws Exception {
if (sua <= 0) {
throw new Exception("Hết sữa");
}
System.out.println("Pha Latte");
}
Lỗi 5: Bắt Exception Rồi Không Xử Lý
❌ SAI:
try {
phaLatte();
} catch (Exception e) {
// Im lặng - rất nguy hiểm!
}
✅ ĐÚNG:
try {
phaLatte();
} catch (Exception e) {
// Ghi log hoặc re-throw
System.err.println("Lỗi: " + e.getMessage());
throw e; // Hoặc xử lý cụ thể
}
💡 Bí Quyết Của Barista
1. Khi Nào Dùng throw?
- Phát hiện dữ liệu không hợp lệ (số âm, null, chuỗi rỗng)
- Phát hiện điều kiện nghiệp vụ vi phạm (hết hàng, ngoài giờ)
- Không thể tiếp tục xử lý logic
2. Khi Nào Dùng throws?
- Phương thức gọi code có thể ném checked exception
- Muốn ủy quyền xử lý cho phương thức gọi
- Khai báo API contract rõ ràng
3. Checked vs Unchecked
-
Checked Exception (IOException, SQLException):
- BẮT BUỘC try-catch hoặc throws
- Lỗi bên ngoài kiểm soát (file, network, database)
-
Unchecked Exception (RuntimeException, NullPointerException):
- KHÔNG BẮT BUỘC xử lý
- Lỗi lập trình (logic sai, dữ liệu sai)
4. Thông Điệp Lỗi Tốt
// ❌ Không rõ ràng
throw new Exception("Lỗi");
// ✅ Cụ thể và hữu ích
throw new IllegalArgumentException(
"Số lượng sữa phải từ 200-500ml. " +
"Giá trị nhận được: " + soLuong + "ml"
);
5. Fail Fast
// ✅ Kiểm tra ngay đầu hàm
public void phaLatte(int sua, int cafe) {
if (sua <= 0) throw new IllegalArgumentException("Thiếu sữa");
if (cafe <= 0) throw new IllegalArgumentException("Thiếu café");
// Logic chính ở đây
// ...
}
6. Custom Exception Cho Business Logic
// Tạo ngoại lệ riêng cho nghiệp vụ quán
public class HetNguyenLieuException extends Exception {
public HetNguyenLieuException(String nguyenLieu) {
super("❌ Hết " + nguyenLieu + "! Vui lòng chọn món khác.");
}
}
// Sử dụng
public void phaLatte() throws HetNguyenLieuException {
if (sua == 0) {
throw new HetNguyenLieuException("sữa");
}
}
🎓 Bạn Đã Học Được
- ✅ throw: Chủ động ném ngoại lệ khi phát hiện vấn đề
- ✅ throws: Khai báo phương thức có thể ném ngoại lệ
- ✅ Checked Exception: BẮT BUỘC xử lý (IOException, SQLException)
- ✅ Unchecked Exception: Không bắt buộc (RuntimeException)
- ✅ Re-throw: Xử lý một phần rồi ném lại cho cấp trên
- ✅ Fail Fast: Kiểm tra điều kiện ngay đầu hàm
- ✅ Thông điệp lỗi: Cụ thể, rõ ràng, hữu ích cho debug
☕ Món Tiếp Theo
Bài tiếp theo: 🎯 Ngoại Lệ Tùy Chỉnh - Custom Exceptions
Tìm hiểu cách tạo các ngoại lệ riêng cho quán café của bạn!
💡 Lời Khuyên Cuối: Hãy sử dụng throw và throws một cách thông minh - không phải mọi lỗi đều cần ném ngoại lệ, nhưng những lỗi quan trọng cần được báo cáo rõ ràng. Một thông điệp lỗi tốt giúp debug nhanh gấp 10 lần! ⚠️☕