บทนำ: ทำไมต้องจัดการกับ Exception?
เมื่อโปรแกรมทำงาน บ่อยครั้งจะเกิดสิ่งไม่คาดคิด เช่น:
- ผู้ใช้ input ข้อมูลผิด
- ไฟล์ที่ต้องการไม่มี
- Network ขาด
- หารด้วย 0
- Array index ออกนอกขอบ
ถ้าไม่จัดการ โปรแกรมจะ “crash” (หยุดทำงาน) อย่างกะทันหัน
Exception Handling คือกลไกที่ให้โปรแกรมของเรา:
- ตรวจหา ข้อผิดพลาด (detect)
- จัดการ ข้อผิดพลาด (handle)
- ทำให้ปลอดภัย (graceful)
ยาวไปอยากเลือกอ่าน
- บทนำ: ทำไมต้องจัดการกับ Exception?
- Exception คืออะไร?
- try-catch: จัดการ Exception
- ตัวอย่างที่ 1: Try-Catch พื้นฐาน
- ตัวอย่างที่ 2: Multiple Catch Blocks
- finally: ทำเสมอไม่ว่าจะเกิด Exception หรือไม่
- ตัวอย่างที่ 3: Try-Catch-Finally
- Custom Exception: สร้าง Exception ของตัวเอง
- ตัวอย่างที่ 4: สร้าง Custom Exception
- ตัวอย่างที่ 5: Try-Catch-Finally กับ Resource
- Try-With-Resources (Java 7+)
- ตัวอย่างที่ 6: Auto-Close Resources
- ตัวอย่างรวม: Application ที่มี Exception Handling
- Best Practices: Exception Handling
- สรุป
Exception คืออะไร?
Exception = สถานการณ์ผิดปกติ ที่เกิดขึ้นระหว่างการทำงาน
textException Hierarchy (ลำดับชั้น):
Throwable
├─ Error (ร้ายแรง - ไม่ควร catch)
│ ├─ OutOfMemoryError
│ └─ StackOverflowError
│
└─ Exception (ปกติ - ควร catch)
├─ Checked Exception (ต้อง handle)
│ ├─ IOException
│ ├─ FileNotFoundException
│ └─ ...
│
└─ Unchecked Exception (ไม่ต้อง handle แต่ควร)
├─ NullPointerException
├─ ArrayIndexOutOfBoundsException
├─ ArithmeticException
└─ ...
try-catch: จัดการ Exception
ตัวอย่างที่ 1: Try-Catch พื้นฐาน
javapublic class Main {
public static void main(String[] args) {
// ==== ก่อน: ไม่จัดการ ====
System.out.println("=== ไม่มี try-catch ===");
int[] numbers = {10, 20, 30};
System.out.println("Index 0: " + numbers[0]);
System.out.println("Index 5: " + numbers[5]); // ❌ Exception!
System.out.println("โปรแกรมจะหยุดที่นี่");
System.out.println("\n=== มี try-catch ===");
// ==== หลัง: มี try-catch ====
try {
int[] numbers2 = {10, 20, 30};
System.out.println("Index 0: " + numbers2[0]);
System.out.println("Index 5: " + numbers2[5]); // Exception!
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("❌ ข้อผิดพลาด: " + e.getMessage());
}
System.out.println("✓ โปรแกรมยังทำงานต่อได้");
}
}
Output:
text=== ไม่มี try-catch ===
Index 0: 10
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
at Main.main(Main.java:...)
=== มี try-catch ===
Index 0: 10
❌ ข้อผิดพลาด: Index 5 out of bounds for length 3
✓ โปรแกรมยังทำงานต่อได้
คำอธิบาย:
try { ... }= ส่วนที่อาจเกิด exceptioncatch (ExceptionType e) { ... }= ถ้าเกิด exception ประเภทนั้น ให้ทำนี้- ถ้ามี try-catch โปรแกรมจะ continue ไม่หยุด
ตัวอย่างที่ 2: Multiple Catch Blocks
javapublic class Main {
public static void main(String[] args) {
// ==== Input จากผู้ใช้ ====
try {
String input = "25a"; // ข้อมูลผิด
int age = Integer.parseInt(input); // ← อาจ throw NumberFormatException
System.out.println("อายุ: " + age);
// สมมติว่า input ถูก แล้วหาร
int divided = 100 / (age - 25); // ← อาจ throw ArithmeticException
System.out.println("ผลลัพธ์: " + divided);
}
catch (NumberFormatException e) {
System.out.println("❌ ข้อผิดพลาด: ต้องป้อนตัวเลข");
System.out.println(" รายละเอียด: " + e.getMessage());
}
catch (ArithmeticException e) {
System.out.println("❌ ข้อผิดพลาด: หารด้วยศูนย์ไม่ได้");
}
catch (Exception e) {
System.out.println("❌ ข้อผิดพลาดอื่น: " + e.getMessage());
}
System.out.println("✓ โปรแกรมยังทำงานต่อ");
}
}
Output:
text❌ ข้อผิดพลาด: ต้องป้อนตัวเลข
รายละเอียด: For input string: "25a"
✓ โปรแกรมยังทำงานต่อ
คำอธิบาย:
- สามารถมี หลาย catch blocks ได้
- Java จะตรวจสอบจากบนลงล่าง เจอตัวแรกที่ match ก็ทำ
- มี catch ทั่วไป
Exceptionไว้ที่ท้ายเพื่อ “catch all”
finally: ทำเสมอไม่ว่าจะเกิด Exception หรือไม่
ตัวอย่างที่ 3: Try-Catch-Finally
javapublic class Main {
public static void main(String[] args) {
// ==== สมมติ: เปิดทรัพยากร (เช่น file) ====
System.out.println("=== Try-Catch-Finally ===");
try {
System.out.println("1. เปิด resource");
// สมมติว่า operation ทำงาน
int result = 10 / 2; // ✓ ไม่มี exception
System.out.println("2. ผลลัพธ์: " + result);
} catch (ArithmeticException e) {
System.out.println("❌ เกิด exception: " + e.getMessage());
} finally {
System.out.println("3. ปิด resource (ทำเสมอ)");
}
System.out.println("\n=== จะเห็นว่า finally ทำเสมอ ===\n");
// ==== ถ้ามี exception ====
try {
System.out.println("4. เปิด resource");
int result = 10 / 0; // ❌ เกิด exception
System.out.println("5. ผลลัพธ์: " + result);
} catch (ArithmeticException e) {
System.out.println("❌ เกิด exception: " + e.getMessage());
} finally {
System.out.println("6. ปิด resource (ทำเสมอ)");
}
}
}
Output:
text=== Try-Catch-Finally ===
1. เปิด resource
2. ผลลัพธ์: 5
3. ปิด resource (ทำเสมอ)
=== จะเห็นว่า finally ทำเสมอ ===
4. เปิด resource
❌ เกิด exception: / by zero
6. ปิด resource (ทำเสมอ)
คำอธิบาย:
finallyทำเสมอ ไม่ว่าจะเกิด exception หรือไม่- ใช้เพื่อ cleanup (เปิด file → ต้องปิด file)
- ใช้เพื่อ ยุติ resources (connection, stream ฯลฯ)
Custom Exception: สร้าง Exception ของตัวเอง
ตัวอย่างที่ 4: สร้าง Custom Exception
บางครั้ง คุณต้อง exception ของตัวเอง เพื่อจัดการสถานการณ์ business logic
java// ==== Custom Exception ====
public class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
public class InsufficientBalanceException extends Exception {
private double shortage;
public InsufficientBalanceException(String message, double shortage) {
super(message);
this.shortage = shortage;
}
public double getShortage() {
return shortage;
}
}
// ==== Class ที่ใช้ custom exception ====
public class Person {
private String name;
private int age;
public Person(String name, int age) throws InvalidAgeException {
this.name = name;
if (age < 0 || age > 150) {
throw new InvalidAgeException("อายุต้องอยู่ระหว่าง 0-150 ปี แต่ได้: " + age);
}
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
// ==== BankAccount ที่ใช้ exception ====
public class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public void withdraw(double amount) throws InsufficientBalanceException {
if (amount > balance) {
double shortage = amount - balance;
throw new InsufficientBalanceException(
"เงินไม่พอ ต้องเพิ่มเติม " + shortage + " บาท",
shortage
);
}
balance -= amount;
System.out.println("✓ ถอนเงิน " + amount + " บาท สำเร็จ");
}
public double getBalance() {
return balance;
}
}
// ==== การใช้งาน ====
public class Main {
public static void main(String[] args) {
// ==== Test InvalidAgeException ====
System.out.println("=== Test InvalidAgeException ===");
try {
Person person = new Person("สมชาย", 200); // ❌ อายุไม่สมเหตุสมผล
} catch (InvalidAgeException e) {
System.out.println("❌ " + e.getMessage());
}
try {
Person person = new Person("สมชาย", 25); // ✓ อายุสมเหตุสมผล
System.out.println("✓ สร้าง Person สำเร็จ: " + person.getName() + " อายุ " + person.getAge());
} catch (InvalidAgeException e) {
System.out.println("❌ " + e.getMessage());
}
// ==== Test InsufficientBalanceException ====
System.out.println("\n=== Test InsufficientBalanceException ===");
BankAccount account = new BankAccount("1234", 1000);
System.out.println("ยอดคงเหลือ: " + account.getBalance() + " บาท");
try {
account.withdraw(500); // ✓ เพียงพอ
System.out.println("ยอดคงเหลือ: " + account.getBalance() + " บาท");
} catch (InsufficientBalanceException e) {
System.out.println("❌ " + e.getMessage());
}
try {
account.withdraw(700); // ❌ ไม่เพียงพอ
} catch (InsufficientBalanceException e) {
System.out.println("❌ " + e.getMessage());
System.out.println(" ขาดอยู่ " + e.getShortage() + " บาท");
}
}
}
Output:
text=== Test InvalidAgeException ===
❌ อายุต้องอยู่ระหว่าง 0-150 ปี แต่ได้: 200
✓ สร้าง Person สำเร็จ: สมชาย อายุ 25
=== Test InsufficientBalanceException ===
ยอดคงเหลือ: 1000.0 บาท
✓ ถอนเงิน 500.0 บาท สำเร็จ
ยอดคงเหลือ: 500.0 บาท
❌ เงินไม่พอ ต้องเพิ่มเติม 200.0 บาท
ขาดอยู่ 200.0 บาท
คำอธิบาย:
class ... extends Exception= สร้าง exception ของตัวเองthrow new ...Exception(...)= โยน exception เมื่อมีปัญหาthrows InvalidAgeExceptionในชื่อ method = บอก caller ว่า method นี้อาจโยน exception นี้- Custom exception ทำให้ code ชัดเจนว่ากำลังจัดการอะไร
ตัวอย่างที่ 5: Try-Catch-Finally กับ Resource
javaimport java.util.Scanner;
public class Main {
public static void main(String[] args) {
// ==== Try-catch-finally เพื่อ cleanup ====
Scanner scanner = null;
try {
scanner = new Scanner("สมชาย\nสมหญิง\nสมศรี");
int count = 0;
while (scanner.hasNextLine() && count < 2) {
String name = scanner.nextLine();
System.out.println("ชื่อ: " + name);
count++;
}
// สมมติว่า operation ลบตัวเลข
int age = Integer.parseInt("25"); // ✓ ถูก
System.out.println("อายุ: " + age);
} catch (NumberFormatException e) {
System.out.println("❌ ข้อผิดพลาด: " + e.getMessage());
} finally {
if (scanner != null) {
scanner.close(); // ← ปิด resource เสมอ
System.out.println("✓ ปิด scanner");
}
}
}
}
Output:
textชื่อ: สมชาย
ชื่อ: สมหญิง
อายุ: 25
✓ ปิด scanner
Try-With-Resources (Java 7+)
ตัวอย่างที่ 6: Auto-Close Resources
Java 7 มีวิธีที่สะดวกกว่า – resources ปิดโดยอัตโนมัติ
javaimport java.util.Scanner;
public class Main {
public static void main(String[] args) {
// ==== Try-with-resources (auto-close) ====
try (Scanner scanner = new Scanner("สมชาย\nสมหญิง")) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println("- " + line);
}
} catch (Exception e) {
System.out.println("❌ " + e.getMessage());
}
// scanner auto-close ที่นี่ (ไม่ต้องใช้ finally)
System.out.println("✓ เสร็จ");
}
}
Output:
text- สมชาย
- สมหญิง
✓ เสร็จ
ข้อดี:
- ไม่ต้องเขียน
finallyเพื่อปิด resource - Resource ปิดโดยอัตโนมัติ
- Code สะอาดกว่า
ตัวอย่างรวม: Application ที่มี Exception Handling
javaimport java.util.Scanner;
// ==== Custom Exception ====
public class InvalidProductException extends Exception {
public InvalidProductException(String message) {
super(message);
}
}
// ==== Product Class ====
public class Product {
private String id;
private String name;
private double price;
public Product(String id, String name, double price) throws InvalidProductException {
if (id == null || id.isEmpty()) {
throw new InvalidProductException("ID ของสินค้าต้องไม่ว่าง");
}
if (price < 0) {
throw new InvalidProductException("ราคาต้องไม่น้อยกว่า 0");
}
this.id = id;
this.name = name;
this.price = price;
}
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
@Override
public String toString() {
return id + " - " + name + " (" + price + " บาท)";
}
}
// ==== Inventory ====
public class Inventory {
private java.util.Map<String, Product> products = new java.util.HashMap<>();
public void addProduct(Product product) throws InvalidProductException {
if (products.containsKey(product.getId())) {
throw new InvalidProductException("สินค้า ID " + product.getId() + " มีอยู่แล้ว");
}
products.put(product.getId(), product);
}
public Product getProduct(String id) throws Exception {
if (!products.containsKey(id)) {
throw new Exception("ไม่พบสินค้า ID: " + id);
}
return products.get(id);
}
public void displayAll() {
System.out.println("=== สินค้าทั้งหมด ===");
for (Product p : products.values()) {
System.out.println("- " + p);
}
}
}
// ==== Main ====
public class Main {
public static void main(String[] args) {
Inventory inventory = new Inventory();
// ==== เพิ่มสินค้า ====
System.out.println("=== เพิ่มสินค้า ===");
try {
inventory.addProduct(new Product("P001", "Notebook", 50));
inventory.addProduct(new Product("P002", "Pen", 10));
inventory.addProduct(new Product("P003", "Pencil", 5));
System.out.println("✓ เพิ่มสินค้าสำเร็จ");
} catch (InvalidProductException e) {
System.out.println("❌ " + e.getMessage());
}
// ==== ลองเพิ่มสินค้าผิด ====
System.out.println("\n=== ลองเพิ่มสินค้าผิด ===");
try {
inventory.addProduct(new Product("P001", "Duplicate", 100));
} catch (InvalidProductException e) {
System.out.println("❌ " + e.getMessage());
}
try {
inventory.addProduct(new Product("P004", "Invalid Price", -10));
} catch (InvalidProductException e) {
System.out.println("❌ " + e.getMessage());
}
// ==== ค้นหาสินค้า ====
System.out.println("\n=== ค้นหาสินค้า ===");
try {
Product p = inventory.getProduct("P001");
System.out.println("✓ พบ: " + p);
} catch (Exception e) {
System.out.println("❌ " + e.getMessage());
}
try {
Product p = inventory.getProduct("P999");
} catch (Exception e) {
System.out.println("❌ " + e.getMessage());
}
// ==== แสดงทั้งหมด ====
inventory.displayAll();
}
}
Output:
text=== เพิ่มสินค้า ===
✓ เพิ่มสินค้าสำเร็จ
=== ลองเพิ่มสินค้าผิด ===
❌ สินค้า ID P001 มีอยู่แล้ว
❌ ราคาต้องไม่น้อยกว่า 0
=== ค้นหาสินค้า ===
✓ พบ: P001 - Notebook (50.0 บาท)
❌ ไม่พบสินค้า ID: P999
=== สินค้าทั้งหมด ===
- P001 - Notebook (50.0 บาท)
- P002 - Pen (10.0 บาท)
- P003 - Pencil (5.0 บาท)
Best Practices: Exception Handling
| ทำได้ | ไม่ทำ |
|---|---|
| ✓ Catch specific exceptions | ❌ Catch generic Exception ไม่ระบุเหตุ |
| ✓ ใช้ finally สำหรับ cleanup | ❌ ลืม cleanup resources |
| ✓ สร้าง custom exception สำหรับ logic | ❌ ใช้ generic exception ทั้งหมด |
| ✓ Log error message ชัดเจน | ❌ Silently ignore exception |
| ✓ ใช้ try-with-resources | ❌ Manual close ใน finally |
| ✓ Re-throw exception เมื่อต้อง | ❌ Swallow exception โดยไม่จำเป็น |
สรุป
Exception Handling คือทักษะที่จำเป็นในการเขียนโปรแกรม ที่มีความเสถียรและปลอดภัย:
- try-catch – จัดการ exception ที่อาจเกิดขึ้น
- finally – ให้ความมั่นใจว่า cleanup ทำเสมอ
- Custom Exception – ทำให้ code ชัดเจนเกี่ยวกับ business logic
- try-with-resources – วิธีสมัยใหม่ให้ resources ปิดอัตโนมัติ
เมื่อใช้ exception handling อย่างถูกต้อง โปรแกรมจะ:
- ไม่ crash อย่างกะทันหัน – มี opportunity ในการทำปกติสมควร
- ชัดเจนกว่า – ผู้อ่าน code รู้ว่า method นี้อาจโยน exception อะไร
- ปลอดภัยกว่า – resources ถูก cleanup อย่างทั่วถึง
- ยืดหยุ่นกว่า – สามารถจัดการสถานการณ์ต่างๆ ได้
Exception handling ไม่ใช่เพื่อ “ลับเรื่อง” แต่เพื่อ “จัดการปัญหา ให้เหมาะสม” ทำให้ user experience ดีขึ้น และระบบเสถียรกว่า
