CodeGym /จาวาบล็อก /สุ่ม /ข้อยกเว้น: การจับและการจัดการ
John Squirrels
ระดับ
San Francisco

ข้อยกเว้น: การจับและการจัดการ

เผยแพร่ในกลุ่ม
สวัสดี! ฉันไม่อยากพูดถึงมัน แต่งานส่วนใหญ่ของโปรแกรมเมอร์กำลังจัดการกับข้อผิดพลาด ส่วนใหญ่มักจะเป็นของเขาหรือเธอเอง ปรากฎว่าไม่มีคนที่ไม่ทำผิด และไม่มีโปรแกรมดังกล่าวเช่นกัน ข้อยกเว้น: การจับและการจัดการ - 1 แน่นอนเมื่อต้องรับมือกับข้อผิดพลาด สิ่งสำคัญคือต้องเข้าใจสาเหตุของข้อผิดพลาด. และมีหลายสิ่งหลายอย่างที่สามารถทำให้เกิดข้อบกพร่องในโปรแกรมได้ เมื่อถึงจุดหนึ่ง ผู้สร้าง Java ถามตัวเองว่าควรทำอย่างไรกับข้อผิดพลาดในการเขียนโปรแกรมที่เป็นไปได้มากที่สุด การหลีกเลี่ยงสิ่งเหล่านี้ไม่สามารถทำได้จริง โปรแกรมเมอร์สามารถเขียนสิ่งที่คุณนึกไม่ถึงได้ :) ดังนั้นเราต้องให้กลไกในการทำงานกับข้อผิดพลาด กล่าวอีกนัยหนึ่ง หากมีข้อผิดพลาดในโปรแกรมของคุณ คุณต้องมีสคริปต์สำหรับสิ่งที่ต้องทำต่อไป โปรแกรมควรทำอย่างไรเมื่อเกิดข้อผิดพลาด วันนี้เราจะทำความคุ้นเคยกับกลไกนี้ เรียกว่า " ข้อยกเว้นใน Java "

ข้อยกเว้นคืออะไร?

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

public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
แต่ถ้าไม่มีไฟล์ดังกล่าวล่ะ! โปรแกรมจะสร้างข้อยกเว้น: FileNotFoundException. เอาต์พุต: ข้อยกเว้นในเธรด "หลัก" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (ระบบไม่พบเส้นทางที่ระบุ) ใน Java ข้อยกเว้นแต่ละรายการจะแสดงด้วยคลาสที่แยกกัน คลาสข้อยกเว้นทั้งหมดเหล่านี้มาจาก "บรรพบุรุษ" ทั่วไป— Throwableคลาสพาเรนต์ ชื่อของคลาสข้อยกเว้นมักจะสะท้อนให้เห็นว่าเหตุใดจึงเกิดข้อยกเว้น:
  • FileNotFoundException(ไม่พบไฟล์)

  • ArithmeticException(มีข้อยกเว้นเกิดขึ้นขณะดำเนินการทางคณิตศาสตร์)

  • ArrayIndexOutOfBoundsException(ดัชนีอยู่นอกขอบเขตของอาร์เรย์) ตัวอย่างเช่น ข้อยกเว้นนี้จะเกิดขึ้นหากคุณพยายามแสดงตำแหน่ง 23 ของอาร์เรย์ที่มีองค์ประกอบเพียง 10 รายการ
โดยรวมแล้ว Java มีคลาสดังกล่าวเกือบ400คลาส! ทำไมมากมาย? เพื่อให้โปรแกรมเมอร์ทำงานได้สะดวกยิ่งขึ้น ลองนึกภาพตามนี้: คุณเขียนโปรแกรม และในขณะที่มันทำงาน มันสร้างข้อยกเว้นที่มีลักษณะดังนี้:

Exception in thread "main"
เอ่อ :/ ไม่ได้ช่วยอะไรมาก ไม่ชัดเจนว่าข้อผิดพลาดหมายถึงอะไรหรือมาจากไหน ไม่มีข้อมูลที่เป็นประโยชน์ที่นี่ แต่คลาสข้อยกเว้นที่หลากหลายใน Java ทำให้โปรแกรมเมอร์มีสิ่งที่สำคัญที่สุด: ประเภทของข้อผิดพลาดและสาเหตุที่เป็นไปได้ (ฝังอยู่ในชื่อคลาส) เป็นอีกสิ่งหนึ่งที่น่าชม

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
ชัดเจนทันทีว่าปัญหาคืออะไรและจะเริ่มขุดเพื่อแก้ปัญหาได้ที่ไหน! ข้อยกเว้น เช่นเดียวกับอินสแตนซ์ของคลาสใดๆ คือวัตถุ

การจับและจัดการข้อยกเว้น

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

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
ผลลัพธ์: ข้อผิดพลาด! ไม่พบไฟล์! เราใส่รหัสของเราในสองช่วงตึก ในบล็อกแรก เราคาดว่าอาจเกิดข้อผิดพลาด "ไม่พบไฟล์" นี่คือtryบล็อก ประการที่สอง เราบอกโปรแกรมว่าควรทำอย่างไรหากเกิดข้อผิดพลาดขึ้น และประเภทข้อผิดพลาดเฉพาะ: FileNotFoundException. หากเราใส่คลาสข้อยกเว้นอื่นในวงเล็บของcatchบล็อก ก็จะFileNotFoundExceptionไม่ถูกตรวจจับ

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
เอาต์พุต: ข้อยกเว้นในเธรด "หลัก" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (ระบบไม่พบเส้นทางที่ระบุ) รหัสในcatchบล็อกไม่ทำงาน เนื่องจากเรา "กำหนดค่า" บล็อกนี้เพื่อจับArithmeticExceptionและรหัสในtryบล็อกโยนประเภทอื่นFileNotFoundException: เราไม่ได้เขียนโค้ดเพื่อจัดการFileNotFoundExceptionดังนั้นโปรแกรมจึงแสดงข้อมูลเริ่มต้นFileNotFoundExceptionสำหรับ ที่นี่คุณต้องใส่ใจกับสามสิ่ง ที่หนึ่ง. เมื่อเกิดข้อยกเว้นในบางบรรทัดในtryบล็อก โค้ดที่ตามมาจะไม่ถูกดำเนินการ การดำเนินการของโปรแกรม "กระโดด" ไปที่catchบล็อก ทันที ตัวอย่างเช่น:

public static void main(String[] args) {
   try {
       System.out.println("Divide by zero");
       System.out.println(366/0);// This line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("will not");
       System.out.println("be");
       System.out.println("executed!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
ผลลัพธ์: หารด้วยศูนย์ โปรแกรมกระโดดไปที่ catch block! ข้อผิดพลาด! คุณไม่สามารถหารด้วยศูนย์! ในบรรทัดที่สองของtryบล็อก เราพยายามหารด้วย 0 ผลลัพธ์ที่ได้ArithmeticExceptionคือ ดังนั้น บรรทัดที่ 3-9 ของtryบล็อกจะไม่ถูกดำเนินการ ดังที่เราได้กล่าวไปแล้ว โปรแกรมจะเริ่มดำเนินการcatchบล็อก ทันที หมายเลขสอง สามารถมีได้หลายcatchช่วงตึก หากโค้ดในtryบล็อกอาจไม่ใช่หนึ่งเดียว แต่มีข้อยกเว้นหลายประเภท คุณสามารถเขียนบล็อกcatchสำหรับแต่ละข้อยกเว้นได้

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
      
       System.out.println("Error! File not found!");
      
   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");
      
   }
}
ในตัวอย่างนี้ เราเขียนสองcatchบล็อก หากFileNotFoundExceptionเกิดขึ้นในtryบล็อก บล็อกแรกcatchจะถูกดำเนินการ หากArithmeticExceptionเกิดขึ้น บล็อกที่สองจะถูกดำเนินการ คุณสามารถเขียนบล็อกได้ 50 catchบล็อกหากต้องการ แน่นอนว่าเป็นการดีกว่าที่จะไม่เขียนโค้ดที่สามารถทำให้เกิดข้อยกเว้นที่แตกต่างกันถึง 50 ชนิด :) ประการที่สาม คุณจะทราบได้อย่างไรว่าโค้ดของคุณอาจมีข้อยกเว้นใดบ้าง คุณอาจจะเดาบางอย่างได้ แต่มันเป็นไปไม่ได้ที่คุณจะเก็บทุกอย่างไว้ในหัว คอมไพเลอร์ Java จึงทราบข้อยกเว้นที่พบบ่อยที่สุดและสถานการณ์ที่อาจเกิดขึ้น ตัวอย่างเช่น หากคุณเขียนโค้ดที่คอมไพเลอร์รู้ว่าอาจมีข้อยกเว้นสองประเภท โค้ดของคุณจะไม่คอมไพล์จนกว่าคุณจะจัดการ เราจะเห็นตัวอย่างด้านล่างนี้ ตอนนี้บางคำเกี่ยวกับการจัดการข้อยกเว้น มี 2 ​​วิธีในการจัดการข้อยกเว้น เราได้พบวิธีแรกแล้ว: เมธอดสามารถจัดการข้อยกเว้นในcatch()บล็อก ได้ มีตัวเลือกที่สอง: เมธอดสามารถโยนข้อยกเว้นขึ้นไปบน call stack ได้อีกครั้ง นั่นหมายความว่าอย่างไร? ตัวอย่างเช่น เรามีคลาสที่มีprintFirstString()เมธอดเดียวกัน ซึ่งอ่านไฟล์และแสดงบรรทัดแรก:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
ในปัจจุบัน โค้ดของเราไม่คอมไพล์ เนื่องจากมีข้อยกเว้นที่ไม่สามารถจัดการได้ ในบรรทัดที่ 1 คุณระบุพาธไปยังไฟล์ คอมไพเลอร์รู้ว่าโค้ดดังกล่าวสามารถสร้างไฟล์FileNotFoundException. ในบรรทัดที่ 3 คุณอ่านข้อความจากไฟล์ กระบวนการนี้อาจส่งผลให้เกิดIOException(ข้อผิดพลาดอินพุต/เอาต์พุต) ได้ง่าย ตอนนี้คอมไพเลอร์พูดกับคุณว่า"เพื่อน ฉันจะไม่อนุมัติโค้ดนี้และฉันจะไม่คอมไพล์จนกว่าคุณจะบอกฉันว่าฉันควรทำอย่างไรหากมีข้อยกเว้นเหล่านี้เกิดขึ้น และสิ่งเหล่านี้อาจเกิดขึ้นได้จากโค้ดที่คุณเขียน !" ไม่มีทางหลีกเลี่ยงได้ คุณต้องจัดการทั้งสองอย่าง! เรารู้เกี่ยวกับวิธีการจัดการกับข้อยกเว้นแรกแล้ว: เราต้องใส่รหัสของเราในบล็อกtryและเพิ่มสองcatchบล็อก:

public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("File input/output error!");
       e.printStackTrace();
   }
}
แต่นี่ไม่ใช่ทางเลือกเดียว เราสามารถโยนข้อยกเว้นให้สูงขึ้นแทนที่จะเขียนโค้ดการจัดการข้อผิดพลาดภายในเมธอด สิ่งนี้ทำได้โดยใช้คำสำคัญthrowsในการประกาศวิธีการ:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
หลังจากคำหลักthrowsเราจะระบุรายการที่คั่นด้วยเครื่องหมายจุลภาคของข้อยกเว้นทุกประเภทที่เมธอดอาจส่ง ทำไม ตอนนี้ ถ้ามีคนต้องการเรียกprintFirstString()ใช้เมธอดในโปรแกรม เขาหรือเธอ (ไม่ใช่คุณ) จะต้องดำเนินการจัดการข้อยกเว้น ตัวอย่างเช่น สมมติว่าที่อื่นในโปรแกรม เพื่อนร่วมงานคนหนึ่งของคุณเขียนเมธอดที่เรียกprintFirstString()เมธอดของคุณ:

public static void yourColleagueMethod() {

   // Your colleague's method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
เราได้รับข้อผิดพลาด! รหัสนี้จะไม่รวบรวม! เราไม่ได้เขียนโค้ดการจัดการข้อยกเว้นในprintFirstString()เมธอด เป็นผลให้งานนี้ตกอยู่บนไหล่ของผู้ที่ใช้วิธีนี้ กล่าวอีกนัยหนึ่งmethodWrittenByYourColleague()เมธอดนี้มี 2 ตัวเลือกเหมือนกัน: ต้องใช้try-catchบล็อกเพื่อจัดการข้อยกเว้นทั้งสองอย่าง หรือโยนใหม่

public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   // The method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
ในกรณีที่สอง เมธอดถัดไปใน call stack—อันที่เรียกmethodWrittenByYourColleague()—จะต้องจัดการกับข้อยกเว้น นั่นเป็นเหตุผลที่เราเรียกสิ่งนี้ว่า "การโยนหรือการข้ามข้อยกเว้น" หากคุณโยนข้อยกเว้นขึ้นด้านบนโดยใช้คีย์เวิร์ด โค้ดthrowsของคุณจะคอมไพล์ ณ จุดนี้ ดูเหมือนว่าคอมไพเลอร์จะพูดว่า"โอเค โอเค โค้ดของคุณมีข้อยกเว้นที่เป็นไปได้มากมาย แต่ฉันจะคอมไพล์ แต่เราจะกลับไปที่การสนทนานี้!" และเมื่อคุณเรียกใช้เมธอดใดก็ตามที่มีข้อยกเว้นที่ไม่ได้จัดการ คอมไพเลอร์จะทำตามสัญญาและเตือนคุณเกี่ยวกับสิ่งเหล่านั้นอีกครั้ง ในที่สุดเราจะพูดถึงfinallyบล็อก (ขออภัยสำหรับการเล่นสำนวน) นี่เป็นส่วนสุดท้ายของtry-catch-finallyการจัดการข้อยกเว้นสามกลุ่ม.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
   }
}
ในตัวอย่างนี้ โค้ดภายในfinallyบล็อกจะถูกดำเนินการในทั้งสองกรณี หากโค้ดในtryบล็อกทำงานเต็มโดยไม่มีข้อยกเว้นใดๆfinallyบล็อกจะทำงานในตอนท้าย หากโค้ดภายในtryบล็อกถูกขัดจังหวะด้วยข้อยกเว้น และโปรแกรมกระโดดไปที่catchบล็อกfinallyบล็อกจะยังคงทำงานต่อจากโค้ดภายในcatchบล็อก เหตุใดจึงจำเป็น วัตถุประสงค์หลักคือการเรียกใช้รหัสบังคับ: รหัสที่ต้องดำเนินการโดยไม่คำนึงถึงสถานการณ์ ตัวอย่างเช่น มันมักจะทำให้ทรัพยากรบางอย่างที่โปรแกรมใช้ว่าง ในโค้ดของเรา เราเปิดสตรีมเพื่ออ่านข้อมูลจากไฟล์และส่งต่อไปยังBufferedReaderอ็อบเจกต์ เราต้องปิดโปรแกรมอ่านของเราและปล่อยทรัพยากร จะต้องดำเนินการนี้ไม่ว่าจะเกิดอะไรขึ้น เมื่อโปรแกรมทำงานตามที่ควร และเมื่อเกิดข้อยกเว้น บล็อกfinallyเป็นสถานที่ที่สะดวกมากในการทำเช่นนี้:

public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
ตอนนี้เรามั่นใจแล้วว่าเราจะดูแลทรัพยากร ไม่ว่าจะเกิดอะไรขึ้นเมื่อโปรแกรมกำลังทำงาน :) นั่นไม่ใช่ทั้งหมดที่คุณจำเป็นต้องรู้เกี่ยวกับข้อยกเว้น การจัดการข้อผิดพลาดเป็นหัวข้อที่สำคัญอย่างยิ่งในการเขียนโปรแกรม มีบทความมากมายที่อุทิศให้กับมัน ในบทเรียนถัดไป เราจะมาดูกันว่ามีข้อยกเว้นประเภทใดบ้างและวิธีสร้างข้อยกเว้นของคุณเอง :) งั้นไว้เจอกันใหม่!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION