Java, обработка исключений и закрытие потоков с помощью try, finally - PullRequest
2 голосов
/ 02 апреля 2012

Я просто хотел посмотреть, есть ли лучший способ справиться с этим. Насколько я понимаю, потоки заключаются в том, что до тех пор, пока вы закрываете поток, все потоки, инкапсулированные в нем, будут закрыты, поэтому я окончательно закрываю TarArchiveOutputStream. Если я получаю FileNotFound в rawDir или archiveDir, я хочу зарегистрировать его, в противном случае все, что я хочу добавить.

public static void createTarGzOfDirectory(File rawDir, File archiveFile) throws IOException {
    FileOutputStream fOut = null;
    BufferedOutputStream bOut = null;
    GzipCompressorOutputStream gzOut = null;
    TarArchiveOutputStream tOut = null;
    try {
        fOut = new FileOutputStream(archiveFile);
        bOut = new BufferedOutputStream(fOut);
        gzOut = new GzipCompressorOutputStream(bOut);
        tOut = new TarArchiveOutputStream(gzOut);
        addFileToTarGz(tOut, rawDir, "");
    } catch (FileNotFoundException e) {
        log.error("File not found: " + e);
    } finally {
        if(tOut != null) {
            tOut.finish();
            tOut.close();
        }
    }

Какие-либо другие соображения или мысли по улучшению вещей?

Ответы [ 3 ]

3 голосов
/ 02 апреля 2012

Мое понимание потоков состоит в том, что до тех пор, пока вы закрываете поток, все потоки, инкапсулированные в нем, будут закрыты ...

Это правильно.

Однако ваш код (эффективно) предполагает, что если tOut равно null, то ни один из других потоков в цепочке не был создан.Это несколько хитрое предположение.Рассмотрим следующую последовательность:

  1. FileOutputStream создан и присвоен fOut.
  2. BufferedOutputStream создан и присвоен bOut.
  3. Конструктор GzipCompressorOutputStream выдает исключение или ошибку.(Возможно, куча переполнена ...).
  4. catch пропущено ... неправильное исключение.
  5. finally проверяет tOut, находит его null, и ничего не делает.

Чистый результат: мы утекли файловый дескриптор / канал, содержащийся в FileOUtputStream.

Ключ к получению этого примера (абсолютно) правильный:понять, какой из этих потоковых объектов содержит критические ресурсы, и убедиться, что этот поток закрыт.Другие потоки, которые не содержат ресурсы, не нужно закрывать.

} finally {
    if (fOut != null) {
        fOut.close();
    }
}

Другой момент заключается в том, что вам нужно переместить вызов tOut.finish() в блок try после addFileToTarGz вызов.

  • Если вызов addFileToTarGz вызывает исключение или если вы не заходите так далеко, вызов finish - пустая трата времени.

  • Вызов finish попытается записать индекс в архив, и это может вызвать IOException.Если это происходит в блоке finally, то любой следующий код в блоке finally, закрывающий цепочку потоков, не будет выполнен ... и дескриптор файла будет пропущен.

1 голос
/ 02 апреля 2012

Хотя это выглядело бы некрасиво и, возможно, вряд ли имело бы место, вы должны закрыть их все каскадом. Да, если вы закроете TarArchiveOutputStream, он должен закрыть базовые потоки. Но, в зависимости от реализации, это не всегда так. Более того, и, вероятно, главным образом, если один из промежуточных конструкторов сгенерирует исключение, tOut будет нулевым, но другие могут не быть. Это означает, что ваши потоки открыты, но вы не закрыли их.

0 голосов
/ 02 апреля 2012

Вы можете объединить все ваши конструкторы вместе, как показано ниже:

    tOut = new TarArchiveOutputStream(new GzipCompressorOutputStream(new BufferedOutputStream(new FileOutputStream(archiveFile))));

И сохраните себе 6 строк инициализации и 3 локальных переменных для отладки.Не всем нравится цеплять вещи таким образом - лично я нахожу это более читабельным, но остальная часть вашей команды может предпочесть это по-вашему.

Что касается закрытия потока, мне это кажется правильным.

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