Benutze ich den Java 7-Versuch mit Ressourcen richtig?

87

Ich erwarte, dass der gepufferte Reader und der Dateireader geschlossen und die Ressourcen freigegeben werden, wenn die Ausnahme ausgelöst wird.

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

Gibt es jedoch eine Voraussetzung für eine catcherfolgreiche Schließung?

BEARBEITEN:

Entspricht der obige Code in Java 7 im Wesentlichen dem folgenden für 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;
}
Gepard
quelle
Nachdem ich Ihre Frage noch einmal gelesen habe, bin ich mir nicht sicher, ob ich sie gut verstehe. Kannst du es bitte erklären?
Maroun
Hallo. Cheetah, ich versuche, die Rolle des ersten catchBeispiels für Java 6 zu verstehen. Das heißt, catch (Exception ex) { throw ex; }es wird nur die Ausnahme erneut ausgelöst, es tut nichts, es kann leicht entfernt werden, ohne dass es verletzt wird. Oder fehlt mir etwas?
Sasha

Antworten:

103

Es ist korrekt und es ist keine catchKlausel erforderlich . Laut Oracle Java 7-Dokument wird die Ressource geschlossen, unabhängig davon, ob tatsächlich eine Ausnahme ausgelöst wird oder nicht.

Sie sollten eine catchKlausel nur verwenden, wenn Sie auf die Ausnahme reagieren möchten. Die catchKlausel wird ausgeführt, nachdem die Ressource geschlossen wurde.

Hier ist ein Ausschnitt aus dem Tutorial von Oracle :

Das folgende Beispiel liest die erste Zeile aus einer Datei. Es verwendet eine Instanz von BufferedReader, um Daten aus der Datei zu lesen. BufferedReader ist eine Ressource, die geschlossen werden muss, nachdem das Programm damit beendet wurde:

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.

... Da die BufferedReader-Instanz in einer try-with-resource-Anweisung deklariert ist, wird sie geschlossen, unabhängig davon, ob die try-Anweisung normal oder abrupt ausgeführt wird (aufgrund der Methode BufferedReader.readLine, die eine IOException auslöst).

BEARBEITEN

In Bezug auf die neu bearbeitete Frage:

Der Code in Java 6 führt den catchund danach den finallyBlock aus. Dies führt dazu, dass die Ressourcen möglicherweise noch im catchBlock geöffnet sind.

In der Java 7-Syntax werden Ressourcen vor dem catchBlock geschlossen , sodass Ressourcen bereits während der catchBlockausführung geschlossen werden . Dies ist im obigen Link dokumentiert:

In einer Try-with-Resources-Anweisung wird jeder catch- oder finally-Block ausgeführt, nachdem die deklarierten Ressourcen geschlossen wurden.

yair
quelle
69

Ihre Verwendung von Try-with-Resources funktioniert in diesem speziellen Fall einwandfrei, ist jedoch im Allgemeinen nicht ganz korrekt. Sie sollten solche Ressourcen nicht verketten, da dies zu unangenehmen Überraschungen führen kann. Angenommen, Sie haben eine variable Puffergröße:

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

Angenommen, etwas ist schiefgegangen und Sie szwaren negativ. In diesem Fall wird Ihre Dateiressource (erstellt über new FileReader(filePath)) NICHT geschlossen.

Um dieses Problem zu vermeiden, sollten Sie jede Ressource wie folgt separat angeben:

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 diesem Fall wird die Initialisierung von brfehlgeschlagen fileimmer noch geschlossen. Weitere Details finden Sie hier und hier .

Andrii Polunin
quelle
Ich versuche zu verstehen, warum die über erstellte Ressource new FileReader(filePath))nicht geschlossen wird, falls eine IllegalArgumentExceptionausgelöst wird, wenn sz negativ ist. Schließt der Versuch mit Ressourcen nicht alle AutoClosableRessourcen, unabhängig von den ausgelösten Ausnahmen?
Prasoon Joshi
3
@PrasoonJoshi Nein, es werden nur .close()die Variablen aufgerufen, die im Initialisierer "Try-with-Resources" deklariert wurden. Deshalb reicht es aus, es in diesem Beispiel in zwei Deklarationen zu trennen.
Mario Carneiro
4
Andrii und @Mario Sie haben beide Recht und Unrecht. Im ersten Beispiel wird der FileReader nicht durch die Try-with-Resource-Logik geschlossen. Wenn der BufferedReader geschlossen wird, wird auch der umschlossene FileReader geschlossen. Schauen Sie sich zum Beweis die Quelle von java.io.BufferedReader.close () an. Infolgedessen sollte der Code aus dem ersten Beispiel bevorzugt werden, da er präziser ist.
Jschreiner
7
@jschreiner Richtig, obwohl Andriis (etwas erfundenes) Problem, bei dem sz < 0der Konstruktor eine Ausnahme auslöst, tatsächlich dazu führt, dass die Ressource ausläuft.
Mario Carneiro
5
@ Mario Ich stimme zu. Der äußere Konstruktor könnte ausfallen und die innere Ressource würde durchgesickert sein. Das habe ich vorher nicht gesehen, danke.
Jschreiner