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

Java io уродливый блок try-finally

Есть ли не так уродливый способ обработать исключение 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();
    }

update: весь приведенный выше код находится в пределах еще одного try-catch, спасибо за предупреждения.

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

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

4b9b3361

Ответ 1

Это правильный 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 сам не выдает исключение или иным образом прерывается ненормально.

Изменить: с Java 7 (и Android SDK 19 - KitKat) теперь есть синтаксис Try with resources, чтобы сделать этот чист. Как это решить в этом вопросе.

Ответ 2

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

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

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

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

Ответ 3

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

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

Оказывается, что catch и finally редко должны использовать один и тот же 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 idiom.

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

Ответ 4

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

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

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

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

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

Ответ 5

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

try (BufferedReader br = new BufferedReader(new FileReader(path)) {
   return br.readLine();
}

Ответ 6

Так как Java 7 существует гораздо лучший способ записать блок try-finally в отношении ресурсов Closeable.

Теперь вы можете создавать свои ресурсы в скобках после ключевого слова try, например:

try (initialize resources here) {
   ...
}

И после завершения кода кода они будут закрыты автоматически. Нет необходимости в части finally.

Пример:

try (
   ZipFile zf = new ZipFile(zipFileName);
   BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset);
) {
    // Enumerate each entry
    for (Enumeration entries = zf.entries(); entries.hasMoreElements();) {
        // Get the entry name and write it to the output file
        String newLine = System.getProperty("line.separator");
        String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
        writer.write(zipEntryName, 0, zipEntryName.length());
    }
}

И после того, как цикл for будет завершен, ресурсы будут закрыты!

Ответ 7

У вас есть в IOUtils некоторые методы closeQuietly.

Ответ 8

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

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

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

Ответ 9

В большинстве случаев исключение '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 */ }
    } 

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

Ответ 10

Используйте

IOUtils.closeNoThrow(myInputStream);

Простой и элегантный.

Ответ 11

Вот мой ответ, надеюсь, гораздо лучше

fooobar.com/questions/52126/...

try {
    fos = new FileOutputStream(new File("..."));
    bos = new BufferedOutputStream(fos);
    oos = new ObjectOutputStream(bos);
}
catch (Exception e) {
}
finally {
    Stream.close(oos,bos,fos);
}


class Stream {

public static void close(AutoCloseable... array) {
    for (AutoCloseable c : array) {
        try {c.close();}
        catch (IOException e) {}
        catch (Exception e) {}
    }
  } 
}

Ответ 12

В С# существует конструкция using, которая автоматически закрывает закрываемые объекты, когда мы оставляем область действия:

using(Stream s = new Stream(filename)) {
  s.read();
}

Я думаю, что это короткая форма для java try-finally block. Java 6 представила интерфейс Closable. Итак, using существует почти. Когда последний шаг будет выполнен в Java 7, это будет потрясающе.