2008-10-01 5 views
30

Хорошо, я делал следующие (имена переменных были изменены):Закрытие 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 слишком много проверенных исключений.

+0

`fis` не может быть пустым в том месте, где вы его тестируете. Он может быть пустым в отсутствующем блоке `finally`, где вы должны его тестировать и закрывать. Но вопрос устарел после введения синтаксиса «try-with-resources». – EJP 2015-04-28 01:14:58

+0

Я изменил код соответственно, чтобы люди не вводили в заблуждение. – 2015-04-28 14:23:57

ответ

38

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

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

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

Эта модель позволяет избежать отводом вокруг с нулевой:

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

Для более подробно о том, как эффективно бороться с близко, прочитать это сообщение в блоге: Java: how not to make a mess of stream handling. Он имеет больше образца кода, больше глубины и охватывает ловушки обертывания закрыть в улов блок.

25

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

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

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

и использовать это с вашего наконец заблокировать.

+0

Ну, это похоже на приглашение NullPointerException ... – 2008-10-01 07:22:48

+0

благодарит за комментарий, изменил его соответственно – squiddle 2008-10-07 15:35:58

0

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

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

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

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

Так squiddle становится:

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

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

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

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

2

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

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

для FileNotFoundException Кроме этого, вы вообще не может «работать вокруг» a IOException. Осталось только сообщить об ошибке, и вы, как правило, будете обрабатывать это в стеке вызовов, поэтому мне лучше распространять исключение.

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

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

1

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

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

Это работает, потому что вызов закрывается второй раз has no effect.

Это зависит от 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 (на который ссылается Макдауэлл).

10

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

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

documenation говорит:

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