Закрытие Java FileInputStream - PullRequest
       9

Закрытие Java FileInputStream

31 голосов
/ 01 октября 2008

Хорошо, я делал следующее (имена переменных были изменены):


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

    ... process ...

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

Недавно я начал использовать FindBugs, что говорит о том, что я неправильно закрываю потоки. Я решил посмотреть, есть ли что-нибудь, что можно сделать с помощью блока finally {}, и тогда я вижу, что да, close () может вызвать IOException Что люди должны делать здесь? Библиотеки Java выдают слишком много проверенных исключений.

Ответы [ 9 ]

43 голосов
/ 01 октября 2008

Для Java 7 и выше try-with-resources следует использовать:

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

Если вы застряли на Java 6 или ниже ...

Этот шаблон позволяет избежать обмана с помощью null :

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

Подробнее о том, как эффективно бороться с close , читайте в этом блоге: Java: как не создавать беспорядок в обработке потоков . Он содержит больше примеров кода, большую глубину и охватывает ловушки close в блоке catch .

26 голосов
/ 01 октября 2008

Что-то вроде следующего должно сделать это, в зависимости от того, бросаете вы или глотаете IOException при попытке закрыть поток.

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)
    {
    }
}
10 голосов
/ 18 мая 2012

Вы можете использовать добавленную функцию try-with-resources JDK7. Он был создан именно для таких вещей

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

Документация гласит:

Оператор try-with-resources обеспечивает закрытие каждого ресурса. в конце выписки.

4 голосов
/ 01 октября 2008

Вы также можете использовать простой статический вспомогательный метод:

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

и используйте это из своего блока finally.

3 голосов
/ 01 октября 2008

Больше нечего добавить, кроме очень незначительного стилистического предложения. В этом случае применяется канонический пример самодокументируемого кода - дать описательное имя переменной игнорируемому IOException, которое вы должны поймать на close().

Таким образом, ответ Squiddle становится:

public static void closeQuietly(InputStream s) {
   try {
      s.close();
   } catch (IOException ignored) {
   }
}
2 голосов
/ 01 октября 2008

В большинстве случаев я считаю, что лучше не отлавливать исключения ввода-вывода и просто использовать try-finally:

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

За исключением FileNotFoundException, вы обычно не можете "обойти" IOException. Осталось только сообщить об ошибке, и вы, как правило, будете обрабатывать ее дальше в стеке вызовов, поэтому я считаю, что лучше распространять исключение.

Поскольку IOException является проверенным исключением, вам придется объявить, что этот код (и любой из его клиентов) throws IOException. Это может быть слишком шумно, или вы не хотите раскрывать детали реализации использования ввода-вывода. В этом случае вы можете заключить весь блок в обработчик исключений, который включает IOException в RuntimeException или абстрактный тип исключения.

Подробно: Мне известно, что приведенный выше код глотает любое исключение из блока try, когда операция close в блоке finally приводит к IOException. Я не думаю, что это большая проблема: как правило, исключение из блока try будет таким же, как IOException, что приводит к сбою close (т. Е. Довольно редко IO работает нормально, а затем терпит неудачу в точке закрытия). Если это вызывает озабоченность, может быть, стоит потрудиться «замолчать» закрытие.

1 голос
/ 23 июля 2011

Следующее решение правильно генерирует исключение, если закрытие завершается неудачно, не скрывая возможное исключение до закрытия.

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

Это работает, потому что повторный вызов close не имеет никакого эффекта .

Это зависит от guava Closeables , но при желании можно написать собственный метод closeQuietly, как показано squiddle (см. Также serg10 ).

Сообщение об ошибке закрытия, в общем случае, важно, потому что close может записать в поток некоторые окончательные байты, например, из-за буферизации. Следовательно, ваш пользователь хочет знать, если это не удалось, или вы, вероятно, хотите действовать как-то. Конечно, это не может быть правдой в конкретном случае FileInputStream, я не знаю (но по уже упомянутым причинам я думаю, что лучше сообщать об ошибке закрытия, если она все равно произойдет).

Приведенный выше код немного сложен для понимания из-за структуры встроенных блоков try. Это можно считать более понятным с помощью двух методов, один из которых генерирует IOException, а другой - его перехватывает. По крайней мере, я бы выбрал это.

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

На основе http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html (на что ссылается Макдауэлл).

0 голосов
/ 01 октября 2008

Вас интересует, прежде всего, получение чистого отчета от FindBugs или наличие кода, который работает? Это не обязательно одно и то же. Ваш оригинальный код в порядке (хотя я бы избавился от лишней проверки if (fis != null), поскольку в противном случае OutOfMemoryException было бы выброшено). FileInputStream имеет метод финализатора, который закроет поток для вас в маловероятном случае, когда вы фактически получите IOException при обработке. Просто не стоит беспокоиться о том, чтобы сделать ваш код более сложным, чтобы избежать крайне маловероятного сценария

  1. вы получаете IOException и
  2. это происходит так часто, что вы начинаете сталкиваться с проблемами с отставанием финализатора.

Редактировать: Если вы получаете так много исключений IOException, что у вас возникают проблемы с очередью финализатора, то вам нужно гораздо больше рыбы, которую нужно жарить! Это о том, чтобы получить представление о перспективе.

0 голосов
/ 01 октября 2008

Надеемся, что когда-нибудь мы получим замыкания в Java, а затем мы потеряем много многословия.

Так что вместо этого в javaIO будет какой-то вспомогательный метод, который вы можете импортировать, он, вероятно, будет иметь интерфейс «Closable», а также блок. Внутри этого вспомогательного метода try {closable.close ()} catch (IOException ex) {// blah} определен раз и навсегда, и тогда вы сможете написать

 Inputstream s = ....;
 withClosable(s) {
    //your code here
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...