Java io ужасная попытка наконец блок

Есть ли не так ужасный способ обработки close() исключение для закрытия обоих потоков затем:

    InputStream in = new FileInputStream(inputFileName);
    OutputStream out = new FileOutputStream(outputFileName);

    try {
        copy(in, out);
    } finally {
        try {
            in.close();
        } catch (Exception e) {
            try {
                // event if in.close fails, need to close the out
                out.close();
            } catch (Exception e2) {}
                throw e; // and throw the 'in' exception
            }
        }
        out.close();
    }

обновление: Весь вышеупомянутый код в еще одной выгоде попытки, спасибо за предупреждения.

НАКОНЕЦ (после ответов):

И хороший служебный метод может быть сделан с помощью, Выполняются Вокруг идиомы (благодарит Tom Hawtin).

43
задан Community 23 May 2017 в 12:26
поделиться

8 ответов

Это правильный idom (и он отлично работает):

   InputStream in = null;
   OutputStream out = null;
   try {
       in = new FileInputStream(inputFileName);
       out = new FileOutputStream(outputFileName);
       copy(in, out);
   finally {
       close(in);
       close(out);
   }

  public static void close(Closeable c) {
     if (c == null) return; 
     try {
         c.close();
     } catch (IOException e) {
         //log the exception
     }
  }

Причина, по которой это работает нормально, заключается в том, что исключение, сгенерированное до того, как вы добрались до finally, будет сгенерировано после завершения вашего кода finally, при условии, что ваш код finally не сам генерирует исключение или иным образом завершает работу ненормально.

Изменить: Начиная с Java 7 (и Android SDK 19 - KitKat), теперь есть синтаксис «Попробовать с ресурсами», чтобы сделать это чище. Как с этим бороться, рассматривается в , этот вопрос .

54
ответ дан 26 November 2019 в 22:29
поделиться

В общем, в IOUtils есть несколько методов closeQuietly .

5
ответ дан 26 November 2019 в 22:29
поделиться

Вы можете реализовать служебный метод:

public final class IOUtil {
  private IOUtil() {}

  public static void closeQuietly(Closeable... closeables) {
    for (Closeable c : closeables) {
        if (c != null) try {
          c.close();
        } catch(Exception ex) {}
    }
  }
}

Тогда ваш код будет сокращен до:

try {
  copy(in, out);
} finally {
  IOUtil.closeQuietly(in, out);
}

Дополнительно

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

32
ответ дан 26 November 2019 в 22:29
поделиться

В большинстве случаев исключение 'in' close () не имеет значения, поэтому:

    try {
      copy(in, out);
    } finally {
    try {  in.close()  }  catch (Exception e) { /* perhaps log it */ }
    try {  out.close() }  catch (Exception e) {/* perhaps log it */ }
    } 

Обычно проглатывание исключений - плохая практика, но в этом случае я думаю, это нормально.

1
ответ дан 26 November 2019 в 22:29
поделиться
try {
    final InputStream in = new FileInputStream(inputFileName);
    try {
        final OutputStream out = new FileOutputStream(outputFileName);    
        try {
            copy(in, out);
            out.flush(); // Doesn't actually do anything in this specific case.
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
} catch (IOException exc) {
    throw new SomeRelevantException(exc);
}

Помните, что открытие потока может вызвать исключение, поэтому вам нужно попытаться между открытием потока (пожалуйста, не делайте какой-то хак с использованием null s. Все может вызвать ошибку (которые не являются экземплярами Exception ).

Оказывается, что catch и , наконец, должны редко использовать один и тот же try .

Начиная с Java SE 7, вы можете писать, используя try-with-resource, чтобы избежать такого большого отступа. Это более или менее делает то же самое, хотя есть скрытые подавленные исключения.

try (
    final InputStream in = new FileInputStream(inputFileName);
    final OutputStream out = new FileOutputStream(outputFileName);    
) {
    copy(in, out);
    out.flush(); // Doesn't actually do anything in this specific case.
} catch (IOException exc) {
    throw new SomeRelevantException(exc);
}

Вы можете использовать идиому Execute Around .

Я считаю, что стандартным хорошим способом копирования является использование NIO transferTo / transferFrom .

18
ответ дан 26 November 2019 в 22:29
поделиться

Я твердо уверен, что в Java 7.0 вам больше не нужно явно закрывать поток самостоятельно. Возможности языка в Java 7

try (BufferedReader br = new BufferedReader(new FileReader(path)) {
   return br.readLine();
}
7
ответ дан 26 November 2019 в 22:29
поделиться

Одна уловка, которую я иногда использую, - это определение метода под названием closeQuietly (Closeable) который проверяет, является ли его аргумент нулевым , а затем закрывает его, игнорируя любые исключения. Но вам нужно быть осторожным, закрывая OutputStreams и Writers таким образом, потому что они могут фактически вызвать исключение, которое имеет значение ; например если окончательный флеш не удастся.

С Java 7 ситуация, вероятно, улучшится. Сообщается, что в ней будет новая конструкция, обеспечивающая более сжатый способ обработки управляемых ресурсов; например потоки, которые необходимо закрыть по завершении работы.

Наконец, вы должны знать, что в вашем примере есть ошибка. Если вызов метода открывает второй поток, первый поток не будет закрыт. Второе открытие нужно сделать внутри блока try .

2
ответ дан 26 November 2019 в 22:29
поделиться

Guava имеет очень хорошие API ввода-вывода, которые устраняют необходимость в этом.Например, ваш пример:

Files.copy(new File(inputFileName), new File(outputFileName));

В более общем плане он использует концепцию InputSupplier s и OutputSupplier s, чтобы разрешить InputStream s и ] OutputStream должны быть созданы в его служебных методах, что дает ему полный контроль над ними, чтобы он мог правильно обрабатывать закрытие.

Кроме того, у него есть Closeables.closeQuietly (Closeable) , который, по сути, является типом метода, предложенного в большинстве ответов.

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

8
ответ дан 26 November 2019 в 22:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: