Zamykanie pliku Java

W porządku, robię co następuje (nazwy zmiennych zostały zmienione):


FileInputStream fis = null;
try
{
    fis = new FileInputStream(file);

    ... process ...

}
catch (IOException e)
{
    ... handle error ...
}
finally
{
    if (fis != null)
        fis.close();
}

Ostatnio zacząłem używać FindBugs, co sugeruje, że nie zamykam poprawnie strumieni. Postanawiam sprawdzić, czy jest coś, co można zrobić za pomocą bloku finally {}, a potem widzę, o tak, close() może rzucić IOException. Co ludzie mają tu robić? Biblioteki Javy rzucają zbyt wiele sprawdzonych WYJĄTKÓW.

Author: Matt H, 2008-10-01

9 answers

Dla Javy 7 i nowszych try-with-resources należy użyć:

try (InputStream in = new FileInputStream(file)) {
  // TODO: work
} catch (IOException e) {
  // TODO: handle error
}

Jeśli utkniesz na Javie 6 lub poniżej...

Ten wzorzec pozwala uniknąć zaznaczenia null :

    try {
        InputStream in = new FileInputStream(file);
        try {
            // TODO: work
        } finally {
            in.close();
        }
    } catch (IOException e) {
        // TODO: error handling
    }

Aby dowiedzieć się więcej o tym, jak skutecznie radzić sobie z Zamknij, przeczytaj ten wpis na blogu: Java: jak nie robić bałaganu w obsłudze strumienia. Ma więcej przykładowego kodu, większą głębokość i obejmuje pułapki owijania Zamknij w bloku catch.

 47
Author: McDowell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-10 19:59:31

Coś takiego jak poniżej powinno to zrobić, od ciebie, czy rzucasz lub połykasz IOException przy próbie zamknięcia strumienia.

FileInputStream fis = null;
try
{
    fis = new FileInputStream(file);

    ... process ...


}
catch (IOException e)
{
    ... blah blah blah ...
}
finally
{
    try
    {
        if (fis != null)
            fis.close();
    }
    catch (IOException e)
    {
    }
}
 26
Author: Max Stewart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 07:05:57

Możesz użyć try-with-resources funkcja dodana JDK7. Został stworzony właśnie po to, aby radzić sobie z tego rodzaju rzeczami

static String readFirstLineFromFile(String path) throws IOException {
  try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }
}

Dokument mówi:

Instrukcja try-with-resources zapewnia zamknięcie każdego Zasobu na końcu Oświadczenia.

 10
Author: Edwin Dalorzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-17 22:35:30

Możesz również użyć prostej statycznej metody pomocniczej:

public static void closeQuietly(InputStream s) {
   if (null == s) {
      return;
   }
   try {
      s.close();
   } catch (IOException ioe) {
      //ignore exception
   }
}

I użyj tego z twojego bloku.

 4
Author: squiddle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-07 15:35:40

Nic dodać nic ująć, poza bardzo drobną sugestią stylistyczną. kanoniczny przykład samodokumentującego się kodu ma zastosowanie w tym przypadku-podaj opisową nazwę zmiennej ignorowanej IOException, którą musisz złapać close().

Więc odpowiedź squiddle ' a staje się:

public static void closeQuietly(InputStream s) {
   try {
      s.close();
   } catch (IOException ignored) {
   }
}
 3
Author: serg10,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 08:12:45

W większości przypadków uważam, że po prostu lepiej Nie złapać wyjątki IO i po prostu użyć try-finally:

final InputStream is = ... // (assuming some construction that can't return null)
try {
    // process is
    ...
} finally {
    is.close();
}

Z wyjątkiem FileNotFoundException, generalnie nie można "obejść" IOException. Jedyne, co pozostało do zrobienia, to zgłosić błąd, i zazwyczaj będzie obsługiwać, że dalej w górę stosu połączeń, więc uważam, że lepiej propagować wyjątek.

Ponieważ IOException jest zaznaczonym wyjątkiem, musisz zadeklarować, że ten kod (i każdy z jego klientów) throws IOException. To może być zbyt głośny, albo możesz nie chcieć ujawniać szczegółów implementacji korzystania z IO. W takim przypadku można zawinąć cały blok za pomocą funkcji obsługi wyjątków, która zawija {[2] } W typ RuntimeException lub abstrakcyjny typ wyjątku.

Detail: zdaję sobie sprawę, że powyższy kod pochłania każdy wyjątek z bloku try, gdy operacja close w bloku finally daje IOException. Nie sądzę, żeby to był duży problem. generalnie wyjątek od bloku try będzie taki sam IOException, że powoduje awarię close (tzn. dość rzadko IO działa poprawnie, a następnie zawodzi w punkcie zamknięcia). Jeśli jest to problemem, może warto "uciszyć" bliskie.

 2
Author: Bruno De Fraine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 10:52:02

Poniższe rozwiązanie poprawnie wyświetla wyjątek, jeśli close nie powiedzie się bez ukrywania możliwego wyjątku przed zamknięciem.

try {
    InputStream in = new FileInputStream(file);
    try {
        // work
        in.close();
    } finally {
        Closeables.closeQuietly(in);
    }
} catch(IOException exc) {
    // kernel panic
}

To działa, ponieważ wywołanie close po raz drugi nie ma żadnego efektu .

Opiera się to na guava Closeables , ale można napisać własną metodę closeQuietly, jeśli jest to preferowane, jak pokazano w squiddle (Zobacz też serg10).

Zgłaszanie błędu close, w ogólnym przypadku, jest ważne, ponieważ close może napisać kilka końcowe bajty do strumienia, np. z powodu buforowania. Dlatego twój użytkownik chce wiedzieć, czy się nie powiodło, czy prawdopodobnie chcesz jakoś działać. Przyznam, że może to nie być prawda w konkretnym przypadku strumienia Plików, Nie wiem (ale z powodów już wymienionych myślę, że lepiej jest zgłosić Bliski błąd, jeśli i tak wystąpi).

Powyższy kod jest nieco trudny do uchwycenia ze względu na strukturę osadzonych bloków try. Można to uznać za jaśniejsze z dwóch metod, jeden, który rzuca IOException i taki, który go łapie. Przynajmniej na to bym się zdecydował.

private void work() throws IOException {
    InputStream in = new FileInputStream(file);
    try {
        // work
        in.close();
    } finally {
        Closeables.closeQuietly(in);
    }
}

public void workAndDealWithException() {
    try {
        work();
    } catch(IOException exc) {
        // kernel panic
    }
}

Na podstawie http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html{[6] / align = "left"/

 1
Author: Olivier Cailloux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:59

Miejmy nadzieję, że pewnego dnia będziemy mieć zamknięcia w Javie, a potem stracimy wiele słowności.

Więc zamiast tego w javaIO będzie jakaś metoda pomocnicza, którą możesz zaimportować, prawdopodobnie będzie to wymagało" zamkniętego " interfejsu, a także bloku. Wewnątrz tej metody pomocniczej TRY {closable.close ()} catch (IOException ex) {//bla} jest zdefiniowany raz na zawsze, a wtedy będziesz mógł napisać

 Inputstream s = ....;
 withClosable(s) {
    //your code here
 }
 0
Author: Lars Westergren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 07:18:51

Czy interesuje Cię przede wszystkim uzyskanie czystego raportu z FindBugs lub posiadanie kodu, który działa? To niekoniecznie to samo. Twój oryginalny kod jest w porządku (chociaż pozbyłbym się zbędnego czeku if (fis != null), ponieważ OutOfMemoryException zostałby wyrzucony w przeciwnym razie). FileInputStream ma metodę finalizera, która zamknie strumień dla Ciebie w mało prawdopodobnym przypadku, gdy faktycznie otrzymasz IOException w przetwarzaniu. Po prostu nie warto się trudzić, aby Twój kod był bardziej wyrafinowane, aby uniknąć niezwykle nieprawdopodobnego scenariusza

  1. otrzymujesz IOException i
  2. zdarza się to tak często, że pojawiają się problemy z zaległościami finalizera.

Edit: jeśli dostajesz tak wiele IOExceptions, że masz problemy z kolejką finalizera, to masz o wiele większe ryby do usmażenia! Chodzi o uzyskanie poczucia perspektywy.

 -4
Author: Dave Griffiths,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-07 10:15:01