Apakah saya menggunakan coba-dengan-sumber daya Java 7 dengan benar

87

Saya mengharapkan pembaca buffer dan pembaca file untuk menutup dan sumber daya dilepaskan jika pengecualiannya dibuang.

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

Namun, apakah ada persyaratan untuk memiliki catchklausul agar penutupan berhasil?

EDIT:

Pada dasarnya, apakah kode di atas di Java 7 sama dengan kode di bawah untuk 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;
}
Cheetah
sumber
Setelah membaca pertanyaan Anda lagi, saya tidak yakin apakah saya memahaminya dengan baik. Bisakah Anda menjelaskannya?
Maroun
Hai. Cheetah, saya mencoba memahami peran yang pertama catchdari contoh Anda untuk Java 6. Yaitu catch (Exception ex) { throw ex; }- ini hanya membuang kembali pengecualian, tidak melakukan apa-apa, dapat dengan mudah dihapus tanpa ada cedera. Atau apakah saya melewatkan sesuatu?
Sasha

Jawaban:

103

Itu benar dan tidak ada persyaratan untuk catchklausul. Oracle java 7 doc mengatakan bahwa sumber daya akan ditutup terlepas dari apakah pengecualian benar-benar dilemparkan atau tidak.

Anda harus menggunakan catchklausa hanya jika Anda ingin bereaksi atas pengecualian tersebut. The catchklausul akan dilaksanakan setelah sumber daya ditutup.

Berikut cuplikan dari tutorial Oracle :

Contoh berikut membaca baris pertama dari sebuah file. Ini menggunakan instance BufferedReader untuk membaca data dari file. BufferedReader adalah sumber daya yang harus ditutup setelah program selesai dengannya:

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.

... Karena instance BufferedReader dideklarasikan dalam pernyataan try-with-resource, ia akan ditutup terlepas dari apakah pernyataan try selesai secara normal atau tiba-tiba (sebagai hasil dari metode BufferedReader.readLine yang menampilkan IOException).

EDIT

Mengenai pertanyaan baru yang diedit:

Kode di Java 6 mengeksekusi catchdan setelah itu finallyblok. Ini menyebabkan sumber daya masih berpotensi dibuka di catchblokir.

Di Jawa 7 sintaks, sumber ditutup sebelum yang catchblok, sehingga sumber daya yang sudah ditutup selama catcheksekusi blok. Ini didokumentasikan di tautan di atas:

Dalam pernyataan coba-dengan-sumber daya, tangkapan apa pun atau blok terakhir dijalankan setelah sumber daya dinyatakan telah ditutup.

yair
sumber
69

Penggunaan try-with-resources Anda akan berfungsi dengan baik dalam kasus khusus ini, tetapi secara umum tidak sepenuhnya benar. Anda tidak boleh merantai sumber daya seperti itu karena dapat menimbulkan kejutan yang tidak menyenangkan. Asumsikan Anda memiliki ukuran buffer variabel:

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);
    } 
}

Asumsikan ada yang tidak beres dan Anda berakhir dengan szsikap negatif. Dalam hal ini sumber file Anda (dibuat melalui new FileReader(filePath)) TIDAK akan ditutup.

Untuk menghindari masalah ini, Anda harus menentukan setiap sumber daya secara terpisah seperti ini:

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);
    } 
}

Dalam kasus ini bahkan jika inisialisasi brgagal filemasih ditutup. Anda dapat menemukan detail lebih lanjut di sini dan di sini .

Andrii Polunin
sumber
Saya mencoba memahami mengapa sumber daya yang dibuat melalui new FileReader(filePath))tidak akan ditutup jika an IllegalArgumentExceptiondilempar ketika sz negatif. Bukankah try-with-resources menutup semua AutoClosableresource terlepas dari pengecualian apa pun yang dilempar?
Prasoon Joshi
3
@PrasoonJoshi Tidak, ini hanya memanggil .close()variabel yang telah dideklarasikan di penginisialisasi try-with-resources. Itulah mengapa memisahkannya menjadi dua deklarasi dalam contoh ini berhasil.
Mario Carneiro
4
Andrii dan @Mario Anda berdua benar dan salah. Dalam contoh pertama, FileReader tidak ditutup oleh logika coba-dengan-sumber daya. Tetapi ketika BufferedReader ditutup, itu akan menutup FileReader yang dibungkus juga. Sebagai bukti, lihat sumber java.io.BufferedReader.close (). Konsekuensinya, kode dari contoh pertama harus diutamakan, karena lebih ringkas.
jschreiner
7
@jschreiner True, meskipun masalah Andrii (agak dibuat-buat) yang sz < 0menyebabkan konstruktor mengeluarkan pengecualian sebenarnya akan menyebabkan sumber daya bocor.
Mario Carneiro
5
@ Mario saya setuju. Konstruktor luar mungkin gagal, dan sumber daya dalam akan bocor. Saya tidak melihat itu sebelumnya, terima kasih.
jschreiner