Java ужасный блок try-finally - PullRequest
       82

Java ужасный блок try-finally

42 голосов
/ 23 апреля 2010

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

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

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

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

Ответы [ 12 ]

52 голосов
/ 23 апреля 2010

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

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

32 голосов
/ 23 апреля 2010

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

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

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

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

18 голосов
/ 23 апреля 2010
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 .

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

8 голосов
/ 23 апреля 2010

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

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

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

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

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

7 голосов
/ 23 апреля 2010

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

try (BufferedReader br = new BufferedReader(new FileReader(path)) {
   return br.readLine();
}
6 голосов
/ 10 октября 2014

Поскольку 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 ресурсы будут закрыты!

5 голосов
/ 23 апреля 2010

Вы имеете, в общем, в IOUtils , некоторые closeQuietly методы.

2 голосов
/ 23 апреля 2010

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

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

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

1 голос
/ 23 апреля 2010

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

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

0 голосов
/ 25 февраля 2016

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

https://stackoverflow.com/a/35623998/2585433

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) {}
    }
  } 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...