ในฐานะ Java developer คุณควรมีความรู้ที่ดีเกี่ยวกับ Java exceptions และ exception handling
บทช่วยสอนนี้ให้ความรู้พื้นฐานที่ทุกโปรแกรมเมอร์ต้องมีเมื่อทำงานกับโปรแกรม Java เริ่มจากเข้าใจว่า Java Exceptions คืออะไร
Java Exceptions คืออะไร
โปรแกรม Java อาจเจอปัญหาทำให้โปรแกรมถูกยกเลิกทันทีระหว่างการรัน ปัญหาเหล่านี้เรียกว่า exceptions
โปรแกรมเมอร์ที่ดีควรสามารถรับรู้ errors ที่อาจเกิดระหว่างการรัน และให้เส้นทางทางเลือกสำหรับโปรแกรมในกรณี exception การปฏิบัตินี้เรียกว่า exception handling
คุณอาจสงสัยว่า ทำไมต้องมี exception handling? ทำไมไม่เขียนโปรแกรมที่ไม่โยน exceptions?
ทำไมต้องมี Exception Handling
การเขียนโปรแกรมที่ไม่โยน exceptions ไม่ได้ง่ายอย่างที่คิด Errors ที่หลีกเลี่ยงไม่ได้ส่วนใหญ่ออกเหนือการควบคุมของโปรแกรมเมอร์
โปรแกรมที่รับ user input प्रवणต่อ exceptions เพราะ invalid input ที่ผู้ใช้ให้ การอ่าน external files ก็เช่นกัน โดยมีโอกาสถูกย้าย เปลี่ยนชื่อ หรือลบโดยแหล่งภายนอกโดยโปรแกรมเมอร์ไม่รู้
ในกรณีเหล่านี้ โปรแกรมต้องจัดการ exception อย่างยืดหยุ่นโดยไม่หยุดการทำงาน
ลำดับชั้นของ Java Exceptions
Exceptions ทั้งหมดใน Java ต้องเป็นลูกของ class Exception ซึ่งเองก็เป็นลูกของ class Throwable
Subclasses หลักสองตัวของ class Exception คือ RuntimeException และ IOException
Exception vs Error
Subclass อีกตัวของ class Throwable คือ class Error แต่ errors ต่างจาก exceptions
Errors บ่งชี้ปัญหาที่ JVM อาจเจอระหว่างการรัน ปัญหาเหล่านี้มักเป็น critical และ irrecoverable Memory leaks และ library incompatibility issues เป็นสาเหตุ error ทั่วไป
StackOverflowError และ OutOfMemoryError เป็นตัวอย่าง Java Errors สองตัว
Checked และ Unchecked Exceptions
เราสามารถแบ่ง Java Exceptions เป็นสองหมวดหลัก: checked และ unchecked exceptions
Checked exceptions คือ exceptions ที่ต้องจัดการในโปรแกรมก่อน compile หากไม่จัดการ โปรแกรมจะไม่ compile โดย Java compiler เรียกว่า compile-time exceptions IOExceptions เป็นตัวอย่าง checked exceptions
Unchecked exceptions คือ exceptions ที่ compiler ไม่สนใจตอน compile ไม่ว่าจะจัดการหรือไม่ โปรแกรมก็ compile ผ่าน เนื่องจากไม่บังคับ exception handling โปรแกรมอาจเจอ RuntimeExceptions ทำให้โปรแกรมหยุด
Classes ทั้งหมดที่ extend RuntimeException class เป็น unchecked exceptions ตัวอย่างสองตัวคือ NullPointerException และ ArrayIndexOutOfBoundsException
เมธอดที่ใช้บ่อยใน Exception Class
เราจะไปดูเมธอดที่ใช้บ่อยใน Java Exception class:
getMessage: คืนข้อความรายละเอียดเกี่ยวกับ exception ที่เกิดprintStackTrace: คืน stack trace ของ exception ที่เกิดtoString: คืนชื่อ class และข้อความจากgetMessage
วิธีจัดการ Exceptions
try-catch
เราสามารถ catch exceptions และจัดการได้อย่างเหมาะสมด้วยบล็อก try-catch ใน Java
ใน syntax นี้ ส่วนของโค้ดที่โยน exception ได้ จะวางใน try block และ catch block(s) จะ catch exception(s) ที่โยนมา และจัดการตามตรรกะที่เราให้
Syntax พื้นฐานของ try-catch block:
try { //exception-prone code } catch(Exception e) { //error handling logic }
ด้วยวิธีนี้ โปรแกรมไม่หยุดเมื่อ exception โยนมา แตจัดการอย่างยืดหยุ่น
เราจะดูวิธาจัดการ IOExceptions ที่โยนโดย class FileReader ในโปรแกรม Java
ตัวอย่าง:
import java.io.FileReader; public class TryCatchBlockExample { public static void main(String[] args) { try { FileReader file = new FileReader("source.txt"); file.read(); } catch(Exception e) { e.printStackTrace(); } } }
ที่นี่ เราใช้ single catch block จัดการ FileNotFoundException ที่โยนตอน instantiate FileReader และ IOException ที่โยนโดย read() method ของ FileReader ทั้งคู่เป็นลูกของ Exception class
เราสามารถใช้ multiple catch statements จับ error types ต่างๆ จากโค้ดใน single try statement เดียวกัน สำหรับตัวอย่างก่อนหน้า ใช้ catch block หนึ่งจับ FileNotFoundException และอีกอันจับ IOException:
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class TryCatchBlockExample { public static void main(String[] args) { try { FileReader file = new FileReader("source.txt"); file.read(); file.close(); } catch(FileNotFoundException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } } }
หาก exception ที่โยนตรงกับที่จับใน catch statement แรก จะจัดการโดยตรรกะใน catch block แรก
หากไม่ตรง จะส่งต่อไป catch statement ที่สอง หากมี catch statements มากกว่าสอง กระบวนการนี้ต่อเนื่องจน exception ถึง catch statement ที่จับ type ของมัน
เนื่องจาก FileNotFoundException เป็น subtype ของ IOException การใช้ catch statement ที่ 2 จับ FileNotFoundException จะไม่ทำงาน จะถูกจับโดย catch statement แรกและไม่ถึง statement ที่ 2
หมายเหตุ: บังคับต้องมี catch statement อย่างน้อยหนึ่งตัวกับ try statement
finally
เมื่อใช้ try-catch block จับ exceptions ในโปรแกรม มีกรณีที่เราอยาก implement logic บางอย่าง ไม่ว่าจะมี exception จับได้หรือไม่ ในกรณีเหล่านี้ ใช้ try-catch-finally block แทนแค่ try-catch
โค้ดใน statement finally จะรันไม่ว่าจะมี exception เกิดขึ้นหรือไม่ finally statement ควรมาอยู่ท้าย try-catch-finally block เสมอ
ตัวอย่าง เมื่อใช้ FileReader อ่านไฟล์ สำคัญต้องปิดไฟล์ที่เปิดอยู่ท้ายการประมวลผล ไม่ว่าจะมี exception หรือไม่ เพื่อให้แน่ใจ ให้วางโค้ดปิดไฟล์ใน finally statement
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class TryCatchFinallyBlockExample { public static void main(String[] args) { FileReader file = null; try { file = new FileReader("source.txt"); file.read(); } catch(FileNotFoundException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } finally { file.close(); } } }
อย่างไรก็ตาม หากคุณพยายาม compile โค้ดข้างต้น จะ compile ไม่ผ่าน เพราะ unhandled IOException เนื่องจาก close() method ของ FileReader class ก็อาจโยน IOExceptions ดังนั้น ต้องวางส่วนนี้ใน try block อีกที:
import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class TryCatchFinallyBlockExample { public static void main(String[] args) { FileReader file = null; try { file = new FileReader("source.txt"); file.read(); } catch(FileNotFoundException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } finally { try { file.close(); } catch(IOException e) { e.printStackTrace(); } } } }
throws
การจัดการ errors ด้วย keyword throws ใน Java เรียบง่าย ในวิธีนี้ คุณไม่ได้จัดการ exception ที่จุดเกิดขึ้นจริง แต่โยน exception ออกจาก current method ไปยัง method ที่เรียก current method แล้วการจัดการ error กลายเป็นหน้าที่ของ outer method
เพื่อโยน exception ออกจาก method ให้ declare ว่า method นี้อาจโยน exception ที่พิจารณา มาดูวิธาจัดการ IOExceptions จาก FileReader ด้วยวิธีนี้
ตัวอย่าง:
import java.io.FileReader; import java.io.IOException; public class ThrowsExample { public void readFile throws IOException { FileReader file = new FileReader("source.txt"); file.read(); file.close(); } }
throw
ไม่เหมือนวิธีอื่นๆ ในรายการนี้ keyword throw ไม่ได้ใช้จัดการ errors แต่เพราะคนส่วนใหญ่สับสน throw กับ throws จึงคิดว่าควรอธิบายที่นี่
Keyword throw ใช้สำหรับ invoke exception อย่างชัดเจน เราสามารถโยน exception ที่ instantiate ใหม่ หรือ exception ที่จับได้ภายใน method
public class ThrowExample { public void invalidate(int amount) throws Exception { if (amount < 500) { throw new Exception("Amount not sufficient"); } } }
User-Defined Exceptions
นอกจาก built-in Java exceptions แล้ว คุณสามารถกำหนด exceptions เองได้ กำหนดเป็น checked หรือ unchecked exceptions เพื่อสร้าง checked exception ใหม่ exception ใหม่ควร extend Exception class
เพื่อสร้าง unchecked exception ให้ extend RuntimeException class
ในตัวอย่างโค้ดต่อไป เราสร้าง user-defined checked exception:
public class InvalidLengthException extends Exception { private int length; private String message; public InvalidLengthException(int length, String message) { this.length=length; this.message=message; } public int getAmount() { return this.length; } public String getMessage() { return this.message; } }
ตอนนี้เราสามารถใช้ exception ข้างต้นใน program logic ได้แบบนี้:
public class InputChecker { private int minLength; private int maxLength; public InputChecker(int minLength, int maxLength) { this.minLength=minLength; this.maxLength=maxLength; } public void checkStringLength(String strInput) throws InvalidLengthException { int strLength = strInput.length(); if (strLength < minLength) { throw new InvalidLengthException(strLength, "Input should have minimum "+minLength+" characters"); } else if (strLength > maxLength){ throw new InvalidLengthException(strLength, "Input should have maximum "+maxLength+" character"); } } }
หากเราตรวจสอบความยาว string ด้วย class InputChecker จะโยน InvalidLengthException หากความยาว string ต่ำกว่าขั้นต่ำ หรือเกินขั้นสูงสุด
public class Main { public static void main(String[] args) { InputChecker ic = new InputChecker(2, 7); try { ic.checkStringLength("longer than the maximum length"); } catch(InvalidLengthException e) { e.printStackTrace(); } } }
ตอนรันโค้ดข้างต้น จะโยน InvalidLengthException และได้ output:
InvalidLengthException: Input should have maximum 7 character at InputChecker.checkStringLength(InputChecker.java:17) at Main.main(Main.java:6)
สรุป
ในบทช่วยสอนนี้ เราให้ introduction สั้นและกระชับเกี่ยวกับ Java Exceptions หวังว่าตอนนี้คุณเข้าใจว่า exceptions คืออะไร และจัดการยังไงใน Java program ของคุณ