Подтвердить что ты не робот

Закрытие Java FileInputStream

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


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 слишком много проверенных исключений.

4b9b3361

Ответ 1

Для 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
    }

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

Ответ 2

Что-то вроде следующего должно это сделать, вплоть до вас, вы бросаете или проглатываете 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)
    {
    }
}

Ответ 3

Вы можете использовать функцию 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

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

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

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

Ответ 5

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

Итак, ответ squild становится:

public static void closeQuietly(InputStream s) {
   try {
      s.close();
   } catch (IOException ignored) {
   }
}

Ответ 6

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

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

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

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

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

Ответ 7

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

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

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

Это зависит от 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 (ссылка на McDowell).

Ответ 8

Мы надеемся, что через некоторое время в Java появятся замыкания, а затем мы потеряем много слов.

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

 Inputstream s = ....;
 withClosable(s) {
    //your code here
 }

Ответ 9

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

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

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