Java - ข้อยกเว้น

ข้อยกเว้น (หรือเหตุการณ์พิเศษ) คือปัญหาที่เกิดขึ้นระหว่างการทำงานของโปรแกรม เมื่อException เกิดขั้นตอนปกติของโปรแกรมหยุดชะงักและโปรแกรม / แอปพลิเคชันหยุดทำงานอย่างผิดปกติซึ่งไม่แนะนำดังนั้นจึงต้องจัดการข้อยกเว้นเหล่านี้

ข้อยกเว้นอาจเกิดขึ้นได้จากหลายสาเหตุ ต่อไปนี้เป็นสถานการณ์บางอย่างที่มีข้อยกเว้นเกิดขึ้น

  • ผู้ใช้ป้อนข้อมูลที่ไม่ถูกต้อง

  • ไม่พบไฟล์ที่ต้องเปิด

  • การเชื่อมต่อเครือข่ายขาดหายไประหว่างการสื่อสารหรือ JVM หน่วยความจำหมด

ข้อยกเว้นเหล่านี้บางส่วนเกิดจากข้อผิดพลาดของผู้ใช้ข้อผิดพลาดของโปรแกรมเมอร์และอื่น ๆ โดยทรัพยากรทางกายภาพที่ล้มเหลวในบางลักษณะ

จากสิ่งเหล่านี้เรามีข้อยกเว้นสามประเภท คุณต้องเข้าใจพวกเขาเพื่อทราบว่าการจัดการข้อยกเว้นทำงานอย่างไรใน Java

  • Checked exceptions- ข้อยกเว้นที่ตรวจสอบแล้วคือข้อยกเว้นที่คอมไพเลอร์ตรวจสอบ (แจ้งเตือน) ณ เวลาคอมไพล์ซึ่งเรียกอีกอย่างว่าข้อยกเว้นเวลาคอมไพล์ ข้อยกเว้นเหล่านี้ไม่สามารถละเลยได้โปรแกรมเมอร์ควรดูแล (จัดการ) ข้อยกเว้นเหล่านี้

ตัวอย่างเช่นถ้าคุณใช้ FileReaderคลาสในโปรแกรมของคุณเพื่ออ่านข้อมูลจากไฟล์หากไม่มีไฟล์ที่ระบุในคอนสตรัคเตอร์FileNotFoundExceptionจะเกิดขึ้นและคอมไพเลอร์จะแจ้งให้โปรแกรมเมอร์จัดการกับข้อยกเว้น

ตัวอย่าง

import java.io.File;
import java.io.FileReader;

public class FilenotFound_Demo {

   public static void main(String args[]) {		
      File file = new File("E://file.txt");
      FileReader fr = new FileReader(file); 
   }
}

หากคุณพยายามรวบรวมโปรแกรมข้างต้นคุณจะได้รับข้อยกเว้นดังต่อไปนี้

เอาต์พุต

C:\>javac FilenotFound_Demo.java
FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
      FileReader fr = new FileReader(file);
                      ^
1 error

Note - ตั้งแต่วิธีการ read() และ close() ของคลาส FileReader พ่น IOException คุณสามารถสังเกตได้ว่าคอมไพลเลอร์แจ้งให้จัดการ IOException พร้อมกับ FileNotFoundException

  • Unchecked exceptions- ข้อยกเว้นที่ไม่ได้ตรวจสอบคือข้อยกเว้นที่เกิดขึ้นในขณะดำเนินการ สิ่งเหล่านี้เรียกอีกอย่างว่าRuntime Exceptions. ซึ่งรวมถึงข้อบกพร่องในการเขียนโปรแกรมเช่นข้อผิดพลาดทางตรรกะหรือการใช้ API อย่างไม่เหมาะสม ข้อยกเว้นรันไทม์จะถูกละเว้นในช่วงเวลาของการคอมไพล์

ตัวอย่างเช่นถ้าคุณได้ประกาศอาร์เรย์ของขนาด 5 ในโปรแกรมของคุณและพยายามที่จะเรียก 6 THองค์ประกอบของอาร์เรย์แล้วArrayIndexOutOfBoundsExceptionexceptionเกิดขึ้น

ตัวอย่าง

public class Unchecked_Demo {
   
   public static void main(String args[]) {
      int num[] = {1, 2, 3, 4};
      System.out.println(num[5]);
   }
}

หากคุณคอมไพล์และรันโปรแกรมข้างต้นคุณจะได้รับข้อยกเว้นดังต่อไปนี้

เอาต์พุต

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
  • Errors- สิ่งเหล่านี้ไม่ใช่ข้อยกเว้น แต่เป็นปัญหาที่เกิดขึ้นนอกเหนือการควบคุมของผู้ใช้หรือโปรแกรมเมอร์ โดยทั่วไปข้อผิดพลาดจะถูกละเว้นในโค้ดของคุณเนื่องจากคุณแทบไม่สามารถทำอะไรเกี่ยวกับข้อผิดพลาดได้ ตัวอย่างเช่นหากเกิดสแต็กล้นจะเกิดข้อผิดพลาด นอกจากนี้ยังถูกละเว้นในช่วงเวลาของการรวบรวม

ลำดับชั้นของข้อยกเว้น

คลาสข้อยกเว้นทั้งหมดเป็นประเภทย่อยของคลาส java.lang.Exception คลาสข้อยกเว้นเป็นคลาสย่อยของคลาส Throwable นอกเหนือจากคลาสข้อยกเว้นยังมีคลาสย่อยอื่นที่เรียกว่า Error ซึ่งได้มาจากคลาส Throwable

ข้อผิดพลาดเป็นเงื่อนไขผิดปกติที่เกิดขึ้นในกรณีที่เกิดความล้มเหลวอย่างรุนแรงสิ่งเหล่านี้ไม่ได้รับการจัดการโดยโปรแกรม Java ข้อผิดพลาดถูกสร้างขึ้นเพื่อระบุข้อผิดพลาดที่สร้างขึ้นโดยสภาพแวดล้อมรันไทม์ ตัวอย่าง: JVM ไม่มีหน่วยความจำ โดยปกติโปรแกรมไม่สามารถกู้คืนจากข้อผิดพลาด

คลาส Exception มีคลาสย่อยหลักสองคลาส: คลาส IOException และคลาส RuntimeException

ต่อไปนี้เป็นรายการที่พบบ่อยที่สุดตรวจสอบและไม่ได้ตรวจสอบJava ในตัวของข้อยกเว้น

วิธีการยกเว้น

ต่อไปนี้เป็นรายการวิธีการสำคัญที่มีอยู่ในคลาส Throwable

ซีเนียร์ วิธีการและคำอธิบาย
1

public String getMessage()

ส่งคืนข้อความโดยละเอียดเกี่ยวกับข้อยกเว้นที่เกิดขึ้น ข้อความนี้เริ่มต้นในตัวสร้าง Throwable

2

public Throwable getCause()

ส่งคืนสาเหตุของข้อยกเว้นที่แสดงโดยวัตถุ Throwable

3

public String toString()

ส่งคืนชื่อของคลาสที่เชื่อมต่อกับผลลัพธ์ของ getMessage ()

4

public void printStackTrace()

พิมพ์ผลลัพธ์ของ toString () พร้อมกับการติดตามสแต็กไปยัง System.err สตรีมเอาต์พุตข้อผิดพลาด

5

public StackTraceElement [] getStackTrace()

ส่งคืนอาร์เรย์ที่มีแต่ละองค์ประกอบบนการติดตามสแต็ก องค์ประกอบที่ดัชนี 0 แสดงถึงด้านบนสุดของ call stack และองค์ประกอบสุดท้ายในอาร์เรย์แสดงถึงวิธีการที่ด้านล่างของ call stack

6

public Throwable fillInStackTrace()

เติมการติดตามสแต็กของอ็อบเจ็กต์ Throwable นี้ด้วยการติดตามสแต็กปัจจุบันโดยเพิ่มข้อมูลก่อนหน้านี้ในการติดตามสแต็ก

การจับข้อยกเว้น

เมธอดจับข้อยกเว้นโดยใช้การรวมกันของ try และ catchคำหลัก บล็อก try / catch ถูกวางไว้รอบ ๆ รหัสที่อาจสร้างข้อยกเว้น โค้ดภายในบล็อก try / catch เรียกว่าโค้ดที่มีการป้องกันและไวยากรณ์สำหรับการใช้ try / catch มีลักษณะดังนี้ -

ไวยากรณ์

try {
   // Protected code
} catch (ExceptionName e1) {
   // Catch block
}

รหัสที่มีแนวโน้มที่จะเกิดข้อยกเว้นจะถูกวางไว้ในบล็อกลอง เมื่อเกิดข้อยกเว้นข้อยกเว้นที่เกิดขึ้นจะถูกจัดการโดย catch block ที่เกี่ยวข้อง การบล็อกการลองทุกครั้งควรตามด้วยบล็อกจับหรือบล็อกในที่สุด

คำสั่งจับเกี่ยวข้องกับการประกาศประเภทของข้อยกเว้นที่คุณพยายามจับ หากมีข้อยกเว้นเกิดขึ้นในรหัสที่ได้รับการป้องกันบล็อก catch (หรือบล็อก) ที่ตามมาจะถูกตรวจสอบ หากชนิดของข้อยกเว้นที่เกิดขึ้นแสดงอยู่ในบล็อก catch ข้อยกเว้นจะถูกส่งผ่านไปยังบล็อก catch เมื่ออาร์กิวเมนต์ถูกส่งไปยังพารามิเตอร์เมธอด

ตัวอย่าง

ต่อไปนี้คืออาร์เรย์ที่ประกาศด้วย 2 องค์ประกอบ จากนั้นรหัสจะพยายามเข้าถึงองค์ประกอบ3 rdของอาร์เรย์ซึ่งทำให้เกิดข้อยกเว้น

// File Name : ExcepTest.java
import java.io.*;

public class ExcepTest {

   public static void main(String args[]) {
      try {
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
}

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

เอาต์พุต

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

หลาย Catch Blocks

บล็อกลองสามารถตามด้วยบล็อกจับหลาย ๆ ไวยากรณ์สำหรับบล็อกการจับหลายรายการมีลักษณะดังต่อไปนี้ -

ไวยากรณ์

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}

ข้อความก่อนหน้านี้แสดงให้เห็นถึงการจับสามบล็อก แต่คุณสามารถมีจำนวนเท่าใดก็ได้หลังจากลองครั้งเดียว หากมีข้อยกเว้นเกิดขึ้นในรหัสที่ได้รับการป้องกันข้อยกเว้นจะถูกส่งไปยังบล็อกแรกในรายการ หากประเภทข้อมูลของข้อยกเว้นที่ส่งตรงกับ ExceptionType1 จะถูกจับที่นั่น ถ้าไม่เช่นนั้นข้อยกเว้นจะส่งผ่านไปยังคำสั่ง catch ที่สอง สิ่งนี้จะดำเนินต่อไปจนกว่าข้อยกเว้นจะถูกจับหรือตกผ่านการจับทั้งหมดซึ่งในกรณีนี้เมธอดปัจจุบันจะหยุดการดำเนินการและข้อยกเว้นจะถูกโยนลงไปที่เมธอดก่อนหน้าบน call stack

ตัวอย่าง

นี่คือส่วนของโค้ดที่แสดงวิธีใช้คำสั่ง try / catch หลายรายการ

try {
   file = new FileInputStream(fileName);
   x = (byte) file.read();
} catch (IOException i) {
   i.printStackTrace();
   return -1;
} catch (FileNotFoundException f) // Not valid! {
   f.printStackTrace();
   return -1;
}

การจับข้อยกเว้นหลายประเภท

ตั้งแต่ Java 7 คุณสามารถจัดการข้อยกเว้นได้มากกว่าหนึ่งรายการโดยใช้บล็อกเดียวคุณลักษณะนี้ช่วยลดความซับซ้อนของโค้ด นี่คือวิธีที่คุณจะทำ -

catch (IOException|FileNotFoundException ex) {
   logger.log(ex);
   throw ex;

คำหลักโยน / โยน

หากเมธอดไม่จัดการกับข้อยกเว้นที่ถูกตรวจสอบเมธอดนั้นจะต้องประกาศโดยใช้ throwsคำสำคัญ. คีย์เวิร์ดพ่นจะปรากฏที่ส่วนท้ายของลายเซ็นของเมธอด

คุณสามารถโยนข้อยกเว้นไม่ว่าจะเป็นการสร้างอินสแตนซ์ใหม่หรือข้อยกเว้นที่คุณเพิ่งจับได้โดยใช้ไฟล์ throw คำสำคัญ.

พยายามทำความเข้าใจความแตกต่างระหว่างการโยนและการโยนคำหลักการโยนใช้เพื่อเลื่อนการจัดการข้อยกเว้นที่ตรวจสอบแล้วและการโยนถูกใช้เพื่อเรียกใช้ข้อยกเว้นอย่างชัดเจน

วิธีการต่อไปนี้ประกาศว่าจะพ่น RemoteException -

ตัวอย่าง

import java.io.*;
public class className {

   public void deposit(double amount) throws RemoteException {
      // Method implementation
      throw new RemoteException();
   }
   // Remainder of class definition
}

เมธอดสามารถประกาศได้ว่ามีข้อยกเว้นมากกว่าหนึ่งข้อซึ่งในกรณีนี้จะมีการประกาศข้อยกเว้นในรายการโดยคั่นด้วยเครื่องหมายจุลภาค ตัวอย่างเช่นวิธีการต่อไปนี้ประกาศว่าจะพ่น RemoteException และ InsufficientFundsException -

ตัวอย่าง

import java.io.*;
public class className {

   public void withdraw(double amount) throws RemoteException, 
      InsufficientFundsException {
      // Method implementation
   }
   // Remainder of class definition
}

สุดท้ายบล็อก

บล็อกสุดท้ายตามด้วย try block หรือ catch block ในที่สุดบล็อกโค้ดจะดำเนินการเสมอโดยไม่คำนึงถึงการเกิดข้อยกเว้น

การใช้บล็อกในที่สุดช่วยให้คุณสามารถเรียกใช้คำสั่งประเภทการล้างข้อมูลที่คุณต้องการดำเนินการได้ไม่ว่าจะเกิดอะไรขึ้นในรหัสที่ได้รับการป้องกัน

ในที่สุดบล็อกจะปรากฏที่ส่วนท้ายของบล็อกจับและมีไวยากรณ์ต่อไปนี้ -

ไวยากรณ์

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}finally {
   // The finally block always executes.
}

ตัวอย่าง

public class ExcepTest {

   public static void main(String args[]) {
      int a[] = new int[2];
      try {
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }finally {
         a[0] = 6;
         System.out.println("First element value: " + a[0]);
         System.out.println("The finally statement is executed");
      }
   }
}

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

เอาต์พุต

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

หมายเหตุต่อไปนี้ -

  • ประโยค catch ไม่สามารถอยู่ได้หากไม่มีคำสั่ง try

  • ไม่บังคับที่จะต้องมีประโยคสุดท้ายเมื่อใดก็ตามที่มีการบล็อก try / catch

  • ไม่สามารถนำเสนอ try block ได้หากไม่มีประโยค catch หรือประโยคสุดท้าย

  • ไม่สามารถแสดงรหัสใด ๆ ระหว่างการลองจับสุดท้ายบล็อก

ลองใช้ทรัพยากร

โดยทั่วไปเมื่อเราใช้ทรัพยากรใด ๆ เช่นสตรีมการเชื่อมต่อ ฯลฯ เราจะต้องปิดอย่างชัดเจนโดยใช้การบล็อกในที่สุด ในโปรแกรมต่อไปนี้เรากำลังอ่านข้อมูลจากไฟล์โดยใช้FileReader และเรากำลังปิดมันโดยใช้การบล็อกในที่สุด

ตัวอย่าง

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadData_Demo {

   public static void main(String args[]) {
      FileReader fr = null;		
      try {
         File file = new File("file.txt");
         fr = new FileReader(file); char [] a = new char[50];
         fr.read(a);   // reads the content to the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }finally {
         try {
            fr.close();
         } catch (IOException ex) {		
            ex.printStackTrace();
         }
      }
   }
}

try-with-resourcesหรือเรียกอีกอย่างว่า automatic resource managementเป็นกลไกการจัดการข้อยกเว้นใหม่ที่นำมาใช้ใน Java 7 ซึ่งจะปิดทรัพยากรที่ใช้ภายในบล็อก try catch โดยอัตโนมัติ

ในการใช้คำสั่งนี้คุณเพียงแค่ต้องประกาศทรัพยากรที่ต้องการภายในวงเล็บและทรัพยากรที่สร้างขึ้นจะถูกปิดโดยอัตโนมัติเมื่อสิ้นสุดบล็อก ต่อไปนี้เป็นไวยากรณ์ของคำสั่ง try-with-resources

ไวยากรณ์

try(FileReader fr = new FileReader("file path")) {
   // use the resource
   } catch () {
      // body of catch 
   }
}

ต่อไปนี้เป็นโปรแกรมที่อ่านข้อมูลในไฟล์โดยใช้คำสั่ง try-with-resources

ตัวอย่าง

import java.io.FileReader;
import java.io.IOException;

public class Try_withDemo {

   public static void main(String args[]) {
      try(FileReader fr = new FileReader("E://file.txt")) {
         char [] a = new char[50];
         fr.read(a);   // reads the contentto the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

ควรคำนึงถึงประเด็นต่อไปนี้ในขณะที่ทำงานกับคำสั่ง try-with-resources

  • ในการใช้คลาสที่มีคำสั่ง try-with-resources ควรใช้ AutoCloseable อินเตอร์เฟซและ close() วิธีการของมันจะถูกเรียกโดยอัตโนมัติเมื่อรันไทม์

  • คุณสามารถประกาศมากกว่าหนึ่งคลาสในคำสั่ง try-with-resources

  • ในขณะที่คุณประกาศหลายคลาสในบล็อก try ของคำสั่ง try-with-resources คลาสเหล่านี้จะปิดตามลำดับย้อนกลับ

  • ยกเว้นการประกาศทรัพยากรภายในวงเล็บทุกอย่างจะเหมือนกับบล็อก try / catch ปกติของบล็อก try

  • ทรัพยากรที่ประกาศในการทดลองใช้จะได้รับการสร้างอินสแตนซ์ก่อนที่จะเริ่มบล็อกการพยายาม

  • ทรัพยากรที่ประกาศในบล็อกลองถูกประกาศโดยปริยายว่าเป็นขั้นสุดท้าย

ข้อยกเว้นที่ผู้ใช้กำหนด

คุณสามารถสร้างข้อยกเว้นของคุณเองใน Java โปรดคำนึงถึงประเด็นต่อไปนี้เมื่อเขียนคลาสข้อยกเว้นของคุณเอง -

  • ข้อยกเว้นทั้งหมดต้องเป็นลูกของ Throwable

  • หากคุณต้องการเขียนข้อยกเว้นที่ตรวจสอบแล้วซึ่งบังคับใช้โดยอัตโนมัติโดยกฎ Handle หรือ Declare คุณต้องขยายคลาส Exception

  • หากคุณต้องการเขียนข้อยกเว้นรันไทม์คุณต้องขยายคลาส RuntimeException

เราสามารถกำหนดคลาส Exception ของเราเองได้ดังนี้ -

class MyException extends Exception {
}

คุณเพียงแค่ต้องขยายที่กำหนดไว้ล่วงหน้า Exceptionคลาสเพื่อสร้าง Exception ของคุณเอง สิ่งเหล่านี้ถือเป็นการตรวจสอบข้อยกเว้น ดังต่อไปนี้InsufficientFundsExceptionคลาสเป็นข้อยกเว้นที่ผู้ใช้กำหนดเองซึ่งขยายคลาส Exception ทำให้เป็นข้อยกเว้นที่ถูกตรวจสอบ คลาสข้อยกเว้นก็เหมือนกับคลาสอื่น ๆ ที่มีฟิลด์และวิธีการที่เป็นประโยชน์

ตัวอย่าง

// File Name InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception {
   private double amount;
   
   public InsufficientFundsException(double amount) {
      this.amount = amount;
   }
   
   public double getAmount() {
      return amount;
   }
}

เพื่อแสดงให้เห็นโดยใช้ข้อยกเว้นที่ผู้ใช้กำหนดเองคลาส CheckingAccount ต่อไปนี้มีวิธีการถอน () ที่พ่น InsufficientFundsException

// File Name CheckingAccount.java
import java.io.*;

public class CheckingAccount {
   private double balance;
   private int number;
   
   public CheckingAccount(int number) {
      this.number = number;
   }
   
   public void deposit(double amount) {
      balance += amount;
   }
   
   public void withdraw(double amount) throws InsufficientFundsException {
      if(amount <= balance) {
         balance -= amount;
      }else {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
   
   public double getBalance() {
      return balance;
   }
   
   public int getNumber() {
      return number;
   }
}

โปรแกรม BankDemo ต่อไปนี้แสดงให้เห็นถึงการเรียกใช้วิธีการฝาก () และถอน () ของ CheckingAccount

// File Name BankDemo.java
public class BankDemo {

   public static void main(String [] args) {
      CheckingAccount c = new CheckingAccount(101);
      System.out.println("Depositing $500...");
      c.deposit(500.00);
      
      try {
         System.out.println("\nWithdrawing $100...");
         c.withdraw(100.00);
         System.out.println("\nWithdrawing $600...");
         c.withdraw(600.00);
      } catch (InsufficientFundsException e) {
         System.out.println("Sorry, but you are short $" + e.getAmount());
         e.printStackTrace();
      }
   }
}

รวบรวมไฟล์ทั้งสามไฟล์ข้างต้นและเรียกใช้ BankDemo สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

เอาต์พุต

Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
         at CheckingAccount.withdraw(CheckingAccount.java:25)
         at BankDemo.main(BankDemo.java:13)

ข้อยกเว้นทั่วไป

ใน Java สามารถกำหนดส่วนจัดเลี้ยงของข้อยกเว้นและข้อผิดพลาดได้สองรายการ

  • JVM Exceptions- นี่คือข้อยกเว้น / ข้อผิดพลาดที่ JVM โยนมาโดยเฉพาะหรือมีเหตุผล ตัวอย่าง: NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException

  • Programmatic Exceptions- ข้อยกเว้นเหล่านี้ถูกโยนทิ้งอย่างชัดเจนโดยแอปพลิเคชันหรือโปรแกรมเมอร์ API ตัวอย่าง: IllegalArgumentException, IllegalStateException