JavaRush /จาวาบล็อก /Random-TH /คอฟฟี่เบรค #203. วิธีจัดการกับข้อยกเว้นโดยใช้คำสั่ง try-w...

คอฟฟี่เบรค #203. วิธีจัดการกับข้อยกเว้นโดยใช้คำสั่ง try-with-resource

เผยแพร่ในกลุ่ม
แหล่งที่มา: สื่อ คู่มือนี้อธิบายประโยชน์ของการลองด้วยทรัพยากรมากกว่าการลองจับในที่สุด นอกจากนี้คุณยังจะได้เรียนรู้ภายใต้เงื่อนไขที่ระงับข้อยกเว้นที่เกิดขึ้น และวิธีใช้การลองใช้ทรัพยากรกับทรัพยากรหลายรายการ คอฟฟี่เบรค #203.  วิธีจัดการกับข้อยกเว้นโดยใช้คำสั่ง try-with-resource - 1โครงสร้างtry with resourcesหรือที่เรียกว่าtry-with-resourcesเป็นกลไกการจัดการข้อยกเว้นใน Java ที่สามารถปิดทรัพยากร เช่น Java InputStream หรือ JDBC Connection ได้โดยอัตโนมัติ เมื่อทำงานกับทรัพยากรเหล่านั้นเสร็จแล้ว ในปี 2011 Oracle ได้เพิ่มความพยายามด้วยทรัพยากรให้กับไวยากรณ์ภาษา Java เพื่อให้แน่ใจว่าออบเจ็กต์ เช่น ซ็อกเก็ตเครือข่าย การเชื่อมต่อฐานข้อมูล และลิงก์ไฟล์และโฟลเดอร์จะถูกปิดอย่างสวยงามหลังจากใช้งาน ความล้มเหลวในการปิดทรัพยากรเหล่านี้หลังจากที่นักพัฒนาเปิดหมายเลขอ้างอิงอาจส่งผลให้เกิดการรั่วไหลของหน่วยความจำ เรียกใช้รูทีนการรวบรวมขยะที่สามารถป้องกันได้ และโอเวอร์เฮดของ CPU บนเซิร์ฟเวอร์

ก่อนจาวา 7

ใน Java หากคุณใช้ทรัพยากร เช่น สตรีมอินพุต/เอาท์พุต คุณจะต้องปิดทรัพยากรเหล่านั้นหลังการใช้งานเสมอ เนื่องจากสามารถส่งข้อ ยกเว้นได้ จึงต้องบรรจุไว้ใน บล็อก try-catch การ ปิดจะต้องเกิดขึ้นใน บล็อก สุดท้าย อย่างน้อยก็เป็นเช่นนั้นจนกระทั่ง Java 7 แต่มีข้อเสียหลายประการ:
  • คุณต้องตรวจสอบว่าทรัพยากรของคุณเป็นโมฆะหรือไม่ก่อนที่จะปิด
  • การปิดตัวเองอาจทำให้เกิดข้อยกเว้น ดังนั้นคุณต้องลองจับ อีกครั้งในบล็อก สุดท้าย ของคุณ
  • โปรแกรมเมอร์มักจะลืมปิดทรัพยากรของตน

จะใช้คำสั่ง try-with-resource ได้อย่างไร?

โอเปอเรเตอร์นี้เดิมเปิดตัวใน Java 7 และแนวคิดก็คือนักพัฒนาไม่จำเป็นต้องกังวลเกี่ยวกับการจัดการทรัพยากรที่พวกเขาใช้ในบล็อกtry -catch-finally อีกต่อไป สิ่งนี้สามารถทำได้โดยการขจัดความจำเป็นใน การบล็อก ในที่สุดซึ่งในทางปฏิบัติแล้วนักพัฒนาใช้เพื่อปิดทรัพยากรเท่านั้น ใน Java คำสั่ง try-with-resourcesคือ คำสั่ง tryที่ประกาศทรัพยากรตั้งแต่หนึ่งรายการขึ้นไป ทรัพยากรคือวัตถุที่ต้องปิดหลังจากโปรแกรมยุติลง เมื่อการเรียกใช้โค้ดออกจากบล็อกtry-with-resourcesทรัพยากรใดๆ ที่เปิดใน บล็อก try-with-resourcesจะถูกปิดโดยอัตโนมัติ โดยไม่คำนึงว่ามีข้อยกเว้นใดๆ เกิดขึ้นภายในบล็อกtry-with-resourcesหรือขณะพยายามปิดทรัพยากรหรือไม่ . หากต้องการใช้คุณลักษณะภาษา Java try-with-resourcesให้ใช้กฎต่อไปนี้:
  • อ อบเจ็กต์ทั้งหมดที่ควบคุมโดย คำสั่ง try-with-resourcesต้องใช้ อินเทอร์เฟซ AutoCloseable
  • สามารถสร้างออบเจ็กต์AutoCloseable ได้ หลายรายการ ใน บล็อก try-with-resources
  • ออบเจ็กต์ที่ประกาศใน คำสั่ง try- with -resourcesจะถูกดำเนินการในtry block แต่จะไม่อยู่ในcatchหรือ บล็อก ในที่สุด
  • เมธอด close()ของอ็อบเจ็กต์ที่ประกาศใน บล็อก try-with-resourcesจะถูกเรียกใช้โดยไม่คำนึงว่ามีข้อยกเว้นเกิดขึ้นที่รันไทม์หรือไม่
  • หากมีข้อยกเว้นเกิดขึ้นใน เมธอด close()ก็อาจถูกจัดประเภทเป็นข้อยกเว้นที่ถูกระงับ
Catchและบล็อกสุดท้ายยังคงสามารถใช้ได้ใน try - with-resource block และจะทำงานเหมือนกับในtry block ปกติ ทรัพยากรที่อ้างอิงโดยวัตถุAutoCloseableจะถูกปิดเสมอหาก ใช้ try-with-resource ซึ่งจะช่วยขจัดปัญหาหน่วยความจำรั่วที่อาจเกิดขึ้น ซึ่งมักเกิดจากการจัดสรรทรัพยากรที่ไม่ดี

ไวยากรณ์

try(declare resources here) {
    // использовать ресурсы
}
catch(FileNotFoundException e) {
    // обработка исключений
}

การใช้งาน try-with-resource ในทางปฏิบัติ

หากต้องการปิดโดยอัตโนมัติ ทรัพยากรจะต้องได้รับการประกาศและเตรียมใช้งานภายในtry :
try (PrintWriter writer = new PrintWriter(new File("test.txt"))) {
    writer.println("Hello World");
}

แทนที่ try-catch-finally ด้วย try-with-resources

วิธีที่ง่ายและชัดเจนในการใช้ ฟังก์ชัน try-with-resourcesคือการแทนที่บล็อกtry-catch-finally แบบดั้งเดิม และ ละเอียด ลองเปรียบเทียบตัวอย่างโค้ดต่อไปนี้ ตัวอย่างแรกคือ บล็อก try-catch-finally ทั่วไป :
Scanner scanner = null;
try {
    scanner = new Scanner(new File("test.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}
และนี่คือโซลูชันใหม่ที่กระชับที่สุดโดยใช้try-with-resources :
try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
}

ความแตกต่างระหว่าง try และ try-with-resource

เมื่อพูดถึงข้อยกเว้น จะมีความแตกต่างระหว่างบล็อก try - catch-finallyและ บล็อก try-with-resources ข้อยกเว้นถูกส่งออกไปทั้งในtry และในท้ายที่สุด block อย่างไรก็ตามวิธีการส่งคืนข้อยกเว้นที่เกิดขึ้นใน บล็อก สุดท้าย เท่านั้น สำหรับtry-with-resourcesหากมีข้อยกเว้นเกิดขึ้น ใน try block และใน คำ สั่งtry-with-resources เมธอดจะส่งคืนข้อยกเว้นที่เกิดขึ้นใน try block ข้อยกเว้นที่เกิดขึ้นจาก บล็อก try-with-resourcesจะถูกระงับ กล่าวคือ เราสามารถพูดได้ว่า บล็อก try-with-resourcesส่งข้อยกเว้นที่ถูกระงับ

เหตุใดฉันจึงควรใช้คำสั่ง try-with-resource

คำสั่งtry-with-resourcesช่วยให้มั่นใจว่าแต่ละทรัพยากรถูกปิดที่ส่วนท้ายของคำสั่ง หากเราไม่ปิดทรัพยากร สิ่งนี้อาจนำไปสู่การรั่วไหลของทรัพยากร และโปรแกรมอาจทำให้ทรัพยากรที่มีอยู่หมดไป สิ่งนี้จะเกิดขึ้นเมื่อคุณใช้ บล็อก try-catch- finally ก่อน Java SE 7 คุณสามารถใช้ บล็อก สุดท้ายเพื่อให้แน่ใจว่าทรัพยากรจะถูกปิด ไม่ว่า คำสั่ง try จะออกตาม ปกติหรือกะทันหันก็ตาม ตัวอย่างต่อไปนี้ใช้ การบล็อก สุดท้าย แทนคำสั่ง try-with-resources :
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {

    FileReader fr = new FileReader(path);
    BufferedReader br = new BufferedReader(fr);
    try {
        return br.readLine();
    } finally {
        br.close();
        fr.close();
    }
}
อาจมีทรัพยากรรั่วไหลในตัวอย่างนี้ โปรแกรมต้องทำมากกว่าการพึ่งพาตัวรวบรวมขยะเพื่อเพิ่มหน่วยความจำของทรัพยากรหลังจากใช้งานเสร็จแล้ว โปรแกรมยังต้องคืนทรัพยากรกลับไปยังระบบปฏิบัติการ โดยปกติโดยการเรียกวิธีการปิดของทรัพยากร อย่างไรก็ตาม หากโปรแกรมไม่ทำเช่นนี้ก่อนที่ตัวรวบรวมขยะจะส่งคืนทรัพยากร ข้อมูลที่จำเป็นในการเพิ่มทรัพยากรจะสูญหาย ทรัพยากรรั่วไหลซึ่งระบบปฏิบัติการยังคงพิจารณาว่าใช้งานอยู่ ในตัวอย่างที่แสดงข้างต้น หาก เมธอด readLineส่งข้อยกเว้นและ คำสั่ง br.close()ใน บล็อก สุดท้าย ส่งข้อยกเว้น แสดงว่า FileReaderมีการรั่วไหล นี่คือสาเหตุที่คุณควรใช้ คำสั่ง try-with-resourcesแทนที่จะ บล็อก ปิดทรัพยากรของโปรแกรม ในที่สุด

อีกตัวอย่างหนึ่ง

ตัวอย่างต่อไปนี้อ่านบรรทัดแรกจากไฟล์ อินสแตนซ์ FileReaderและBufferedReaderใช้ในการอ่านข้อมูล ทรัพยากรเหล่านี้เป็นทรัพยากรที่ต้องปิดหลังจากออกจากโปรแกรม
import java.io.*;
class Main {
  public static void main(String[] args) {
    String line;
    try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    }
  }
}
อย่างที่คุณเห็น ทรัพยากรที่ประกาศในคำ สั่ง try-with-resourcesคือBufferedReader ข้อความประกาศสำหรับทรัพยากรนี้จะปรากฏอยู่ในวงเล็บหลังคีย์เวิร์ดtry คลาสBufferedReaderใน Java SE 7 และใหม่กว่าใช้ อิน เท อร์เฟซ java.lang.AutoCloseable เนื่องจาก อินสแตนซ์ BufferedReaderได้รับการประกาศใน คำสั่ง try-with-resource อินสแตนซ์เหล่านั้นจะถูกปิดโดยไม่คำนึงว่า คำสั่ง try จะออก ตามปกติหรือกะทันหัน (หาก เมธอด BufferedReader.readLine()ส่งIOException )

ข้อยกเว้นที่ถูกระงับ

หาก บล็อก tryส่งข้อยกเว้น และมีข้อยกเว้นอย่างน้อยหนึ่งรายการปรากฏใน บล็อก try-with-resourcesดังนั้นข้อยกเว้นที่เกิดจาก บล็อก try-with-resourcesจะถูกระงับ กล่าวอีกนัยหนึ่ง เราสามารถพูดได้ว่าข้อยกเว้นที่เกิดจากtry-with-resourcesจะถูกระงับข้อยกเว้น คุณสามารถตรวจจับข้อยกเว้นเหล่านี้ได้โดยใช้เมธอดgetSuppress() ของ คลาสThrowable ในตัวอย่างที่แสดงก่อนหน้านี้ มีข้อยกเว้นเกิดขึ้นโดย คำสั่ง try-with-resourcesภายใต้เงื่อนไขต่อไปนี้:
  • ไม่พบไฟล์test.txt
  • การปิดวัตถุBufferedReader
ข้อยกเว้นสามารถถูกส่งออกจากtry block เนื่องจากการอ่านไฟล์อาจล้มเหลวได้จากหลายสาเหตุเมื่อใดก็ได้ หากข้อยกเว้นถูกส่งออกจากทั้งtry block และ คำสั่ง try-with-resourcesในกรณีแรก ข้อยกเว้นจะถูกส่งออกไป และในกรณีที่สอง ข้อยกเว้นจะถูกระงับ

ได้รับการระงับข้อยกเว้น

ใน Java 7 และใหม่กว่า สามารถรับข้อยกเว้นที่ถูกระงับได้โดยการเรียก เมธอด Throwable.getSuppressed()จากข้อยกเว้นที่ส่งมาจากtry block getSuppress()ส่งคืนอาร์เรย์ที่มีข้อยกเว้นทั้งหมดที่ถูกระงับโดย คำ สั่งtry-with-resources ถ้าไม่มีการระงับข้อยกเว้นหรือปิดใช้งานการระงับ อาร์เรย์ว่างจะถูกส่งกลับ นี่คือตัวอย่างของการรับข้อยกเว้นที่ถูกระงับในcatch block :
catch(IOException e) {
  System.out.println("Thrown exception=>" + e.getMessage());
  Throwable[] suppressedExceptions = e.getSuppressed();
  for (int i=0; i" + suppressedExceptions[i]);
  }
}

ประโยชน์ของการใช้ทรัพยากรแบบลองกับ

  • สามารถอ่านและเขียนโค้ดได้ง่าย
  • การจัดการทรัพยากรอัตโนมัติ
  • จำนวนบรรทัดของโค้ดลดลง
  • เมื่อมีการเปิดทรัพยากรหลายรายการในtry-with-resourcesทรัพยากรเหล่านั้นจะถูกปิดในลำดับย้อนกลับเพื่อหลีกเลี่ยงปัญหาการขึ้นต่อกัน
และแน่นอนว่าในที่สุด บล็อกก็ไม่จำเป็นต้องปิดทรัพยากร อีก ต่อไป ก่อนหน้านี้ ก่อน Java 7 เราต้องใช้ บล็อก สุดท้ายเพื่อให้แน่ใจว่าทรัพยากรถูกปิดเพื่อหลีกเลี่ยงการรั่วไหลของทรัพยากร นี่คือโปรแกรมที่คล้ายกับตัวอย่างแรก ในโปรแกรมนี้ เราใช้ บล็อก สุดท้ายเพื่อปิดทรัพยากร
import java.io.*;
class Main {
  public static void main(String[] args) {
    BufferedReader br = null;
    String line;
    try {
      System.out.println("Entering try block");
      br = new BufferedReader(new FileReader("test.txt"));
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    } finally {
      System.out.println("Entering finally block");
      try {
        if (br != null) {
          br.close();
        }
      } catch (IOException e) {
        System.out.println("IOException in finally block =>"+e.getMessage());
      }
    }
  }
}
บทสรุป:
เข้าสู่การลองบล็อก Line =>line จากไฟล์ test.txt เข้าสู่การบล็อกในที่สุด
ดังที่คุณเห็นจากตัวอย่างด้านบน การใช้ บล็อก สุดท้ายเพื่อล้างทรัพยากรจะเพิ่มความซับซ้อนให้กับโค้ด สังเกตเห็นtry...catch blockใน บล็อก สุดท้าย ? เนื่องจาก IOException สามารถเกิดขึ้นได้เมื่อปิด อินสแตนซ์ BufferedReaderภายใน บล็อก สุดท้ายดังนั้นจึงถูกจับและจัดการด้วย คำสั่งtry-with-resourcesดำเนินการจัดการทรัพยากรโดยอัตโนมัติ เราไม่จำเป็นต้องปิดทรัพยากรอย่างชัดเจนเนื่องจาก JVM ปิดทรัพยากรเหล่านั้นโดยอัตโนมัติ ทำให้โค้ดอ่านง่ายขึ้นและเขียนได้ง่ายขึ้น

ลองกับทรัพยากรที่มีหลายทรัพยากร

เราสามารถประกาศทรัพยากรหลายรายการใน บล็อก try-with-resourcesได้โดยแยกทรัพยากรเหล่านั้นด้วยเครื่องหมายอัฒภาค:
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    while (scanner.hasNext()) {
	writer.print(scanner.nextLine());
    }
}

Java 9 - ตัวแปรสุดท้ายที่มีประสิทธิภาพ

ก่อน Java 9 เราสามารถใช้เฉพาะตัวแปรใหม่ภายในtry-with-resources block :
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    // omitted
}
โปรดทราบว่านี่ค่อนข้างละเอียดเมื่อประกาศทรัพยากรหลายรายการ ตั้งแต่ Java 9 (อัปเดต JEP 213) เราสามารถใช้ตัวแปรสุดท้ายหรือ ตัวแปร สุดท้ายที่มีประสิทธิผลภายใน บล็อก try-with-resources :
final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) {
    // omitted
}
พูดง่ายๆ ก็คือ ตัวแปรถือเป็นที่สิ้นสุด อย่างมีประสิทธิภาพ หากไม่มีการแก้ไขหลังจากการมอบหมายครั้งแรก แม้ว่าจะไม่ได้ทำเครื่องหมายไว้อย่างชัดเจนว่าเป็นที่สิ้นสุด ก็ตาม ดังที่แสดงไว้ด้านบน ตัวแปร สแกนเนอร์ได้รับการประกาศขั้นสุดท้าย อย่างชัดเจน เพื่อให้เราสามารถใช้กับ บล็อก try-with-resourcesได้ แม้ว่าตัวแปรตัวเขียนจะไม่เป็นขั้นสุดท้าย อย่างชัดเจน แต่จะไม่เปลี่ยนแปลงหลังจากการมอบหมายครั้งแรก ดังนั้นเราจึงสามารถใช้ ตัวแปร ตัวเขียนได้ ฉันหวังว่าวันนี้คุณจะเข้าใจวิธีจัดการกับข้อยกเว้นโดยใช้คำ สั่ง try-with-resource มากขึ้น มีความสุขในการเรียนรู้!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION