Sto usando correttamente Java 7 try-with-resources


87

Mi aspetto che il lettore bufferizzato e il lettore di file si chiudano e le risorse vengano rilasciate se viene generata l'eccezione.

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

Tuttavia, è necessario disporre di una catchclausola per la chiusura con successo?

MODIFICARE:

In sostanza, il codice sopra in Java 7 è equivalente al seguente per 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;
}

Dopo aver letto di nuovo la tua domanda, non sono sicuro di averla capita bene. Puoi spiegarlo per favore?
Maroun

Ciao. Cheetah, sto cercando di capire il ruolo del primo catchdel tuo esempio per Java 6. Cioè catch (Exception ex) { throw ex; }, si tratta solo di rilanciare l'eccezione, non sta facendo nulla, può essere facilmente rimosso senza alcun danno. O mi sta sfuggendo qualcosa?
Sasha

Risposte:


103

È corretto e non è richiesta alcuna catchclausola. Oracle java 7 doc afferma che la risorsa verrà chiusa indipendentemente dal fatto che venga effettivamente generata un'eccezione o meno.

Dovresti usare una catchclausola solo se vuoi reagire all'eccezione. La catchclausola verrà eseguita dopo la chiusura della risorsa.

Ecco uno snippet dal tutorial di Oracle :

L'esempio seguente legge la prima riga da un file. Utilizza un'istanza di BufferedReader per leggere i dati dal file. BufferedReader è una risorsa che deve essere chiusa dopo che il programma ha terminato con essa:

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.

... Poiché l'istanza BufferedReader è dichiarata in un'istruzione try-with-resource, verrà chiusa indipendentemente dal fatto che l'istruzione try venga completata normalmente o in modo brusco (come risultato del metodo BufferedReader.readLine che genera un'IOException).

MODIFICARE

Per quanto riguarda la nuova domanda modificata:

Il codice in Java 6 esegue catchil finallyblocco e successivamente il blocco. Ciò fa sì che le risorse siano ancora potenzialmente aperte nel catchblocco.

Nella sintassi Java 7, le risorse vengono chiuse prima del catchblocco, quindi le risorse sono già chiuse durante l' catchesecuzione del blocco. Questo è documentato nel link sopra:

In un'istruzione try-with-resources, qualsiasi blocco catch o finalmente viene eseguito dopo che le risorse dichiarate sono state chiuse.


69

Il tuo utilizzo di try-with-resources funzionerà bene in questo caso particolare, ma in generale non è del tutto corretto. Non dovresti concatenare le risorse in questo modo perché potrebbe portare a spiacevoli sorprese. Supponiamo di avere una dimensione del buffer variabile:

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

Supponi che qualcosa sia andato storto e che tu abbia finito per szessere negativo. In questo caso la tua risorsa file (creata tramite new FileReader(filePath)) NON verrà chiusa.

Per evitare questo problema è necessario specificare ciascuna risorsa separatamente in questo modo:

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

In questo caso, anche se l'inizializzazione dei brfail fileviene comunque chiusa. Puoi trovare maggiori dettagli qui e qui .


Sto cercando di capire perché la risorsa creata tramite new FileReader(filePath))non si chiude nel caso in cui IllegalArgumentExceptionvenga lanciato un quando sz è negativo. La prova con le risorse non chiude tutte le AutoClosablerisorse indipendentemente dalle eccezioni lanciate?
Prasoon Joshi

3
@PrasoonJoshi No, chiama solo .close()le variabili che sono state dichiarate nell'inizializzatore try-with-resources. Ecco perché separarlo in due dichiarazioni in questo esempio fa il trucco.
Mario Carneiro

4
Andrii e @Mario Avete sia ragione che torto. Nel primo esempio, FileReader non è chiuso dalla logica try-with-resource. Ma quando il BufferedReader è chiuso, chiuderà anche il FileReader avvolto. Per prova, dai un'occhiata al sorgente di java.io.BufferedReader.close (). Di conseguenza, il codice del primo esempio dovrebbe essere preferito, perché è più conciso.
jschreiner

7
@jschreiner Vero, sebbene il problema (un po 'artificioso) di Andrii in cui sz < 0il costruttore lancia un'eccezione causerà in effetti la perdita della risorsa.
Mario Carneiro

5
@mario sono d'accordo. Il costruttore esterno potrebbe non riuscire e la risorsa interna verrebbe persa. Non l'ho visto prima, grazie.
jschreiner
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.