ฉันใช้ Java 7 try-with-resources อย่างถูกต้องหรือไม่


87

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

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    try (BufferedReader br = new BufferedReader(new FileReader(filePath)))
    {
        return read(br);
    } 
}

อย่างไรก็ตามจำเป็นต้องมีcatchข้อสำหรับการปิดสำเร็จหรือไม่?

แก้ไข:

โดยพื้นฐานแล้วคือโค้ดด้านบนใน Java 7 เทียบเท่ากับด้านล่างสำหรับ Java 6:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{

    BufferedReader br = null;

    try
    {
        br = new BufferedReader(new FileReader(filePath));

        return read(br);
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        try
        {
            if (br != null) br.close();
        }
        catch(Exception ex)
        {
        }
    }

    return null;
}

หลังจากอ่านคำถามของคุณอีกครั้งฉันไม่แน่ใจว่าเข้าใจดีหรือไม่ คุณช่วยอธิบายได้ไหม?
Maroun

สวัสดี. Cheetah ฉันกำลังพยายามทำความเข้าใจบทบาทของตัวอย่างแรกcatchของคุณสำหรับ Java 6 เช่นcatch (Exception ex) { throw ex; }- เพียงแค่โยนข้อยกเว้นซ้ำไม่ทำอะไรเลยสามารถลบออกได้อย่างง่ายดายโดยไม่ต้องบาดเจ็บใด ๆ หรือฉันขาดอะไรไป?
Sasha

คำตอบ:


103

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

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

นี่คือตัวอย่างจากบทช่วยสอนของ Oracle :

ตัวอย่างต่อไปนี้อ่านบรรทัดแรกจากไฟล์ ใช้อินสแตนซ์ของ BufferedReader เพื่ออ่านข้อมูลจากไฟล์ BufferedReader เป็นทรัพยากรที่ต้องปิดหลังจากโปรแกรมเสร็จสิ้น:

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
} // In this example, the resource declared in the try-with-resources statement is a BufferedReader.

... เนื่องจากอินสแตนซ์ BufferedReader ถูกประกาศในคำสั่ง try-with-resource มันจะถูกปิดโดยไม่คำนึงว่าคำสั่ง try จะเสร็จสมบูรณ์ตามปกติหรือในทันที (อันเป็นผลมาจากวิธี BufferedReader.readLine ที่ขว้าง IOException)

แก้ไข

เกี่ยวกับคำถามที่แก้ไขใหม่:

โค้ดใน Java 6 เรียกใช้งานcatchและหลังจากนั้นfinallyบล็อก ซึ่งทำให้ทรัพยากรยังคงเปิดอยู่ในcatchบล็อก

ใน Java 7 ไวยากรณ์ทรัพยากรถูกปิดก่อนcatchบล็อกเพื่อให้ทรัพยากรที่ปิดไปแล้วในระหว่างcatchการดำเนินการบล็อก นี่คือเอกสารในลิงค์ด้านบน:

ในคำสั่ง try-with-resources การดักจับหรือการบล็อกในที่สุดจะถูกเรียกใช้หลังจากปิดทรัพยากรที่ประกาศแล้ว


69

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

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    int sz = /* get buffer size somehow */
    try (BufferedReader br = new BufferedReader(new FileReader(filePath), sz))
    {
        return read(br);
    } 
}

สมมติว่ามีบางอย่างผิดพลาดและคุณszถูกมองในแง่ลบ ในกรณีนี้ทรัพยากรไฟล์ของคุณ (สร้างผ่านnew FileReader(filePath)) จะไม่ถูกปิด

เพื่อหลีกเลี่ยงปัญหานี้คุณควรระบุแต่ละทรัพยากรแยกกันดังนี้:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    int sz = /* get buffer size somehow */
    try (FileReader file = new FileReader(filePath);
         BufferedReader br = new BufferedReader(file, sz))
    {
        return read(br);
    } 
}

ในกรณีนี้แม้ว่าการเริ่มต้นbrล้มเหลวfileจะยังคงถูกปิด คุณสามารถค้นหารายละเอียดเพิ่มเติมได้ที่นี่และที่นี่


ฉันพยายามทำความเข้าใจว่าเหตุใดทรัพยากรที่สร้างผ่านnew FileReader(filePath))จึงไม่ปิดในกรณีที่มีการIllegalArgumentExceptionโยนเมื่อ sz เป็นลบ การทดลองใช้กับทรัพยากรไม่ปิดทรัพยากรทั้งหมดAutoClosableโดยไม่คำนึงถึงข้อยกเว้นใด ๆ
Prasoon Joshi

3
@PrasoonJoshi ไม่ใช่มันเรียกเฉพาะ.close()ตัวแปรที่ประกาศไว้ในตัวเริ่มต้นแบบลองใช้ทรัพยากรเท่านั้น นั่นเป็นเหตุผลว่าทำไมการแยกมันออกเป็นสองประกาศในตัวอย่างนี้จึงเป็นการหลอกลวง
Mario Carneiro

4
Andrii และ @Mario คุณทั้งถูกและผิด ในตัวอย่างแรก FileReader ไม่ได้ถูกปิดโดยตรรกะ try-with-resource แต่เมื่อปิด BufferedReader มันจะปิด FileReader ที่ถูกห่อด้วยเช่นกัน สำหรับการพิสูจน์โปรดดูที่มาของ java.io.BufferedReader.close () ด้วยเหตุนี้โค้ดจากตัวอย่างแรกควรเลือกใช้เนื่องจากมีความกระชับมากกว่า
jschreiner

7
@jschreiner True แม้ว่าปัญหาของ Andrii (ที่ค่อนข้างมีการแก้ไข) ซึ่งsz < 0ทำให้ตัวสร้างโยนข้อยกเว้นจะทำให้ทรัพยากรรั่วไหล
Mario Carneiro

5
@mario ฉันเห็นด้วย ตัวสร้างภายนอกอาจล้มเหลวและทรัพยากรภายในจะรั่วไหล ฉันไม่เห็นมาก่อนขอบคุณ
jschreiner
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.