Правильный способ закрыть вложенные потоки и записи в Java [дубликат]



этот вопрос уже есть ответ здесь:



Примечание: этот вопрос и большинство его ответов датируются до выпуска Java 7. Java 7 предоставляет Автоматическое Управление Ресурсами функциональность для того, чтобы сделать это легко. Если вы используете Java 7 или более позднюю версию, вы должны перейти к ответу Росса Джонсона.





что считается лучшим, наиболее полным способом закрытия вложенных потоков в Java? Например, рассмотрим настройку:



FileOutputStream fos = new FileOutputStream(...)
BufferedOS bos = new BufferedOS(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);


Я понимаю, что близкая операция должна быть застрахована (вероятно, с помощью пункта finally). Мне интересно, нужно ли явно убедиться, что вложенные потоки закрыты, или этого достаточно, чтобы просто убедиться чтобы закрыть внешний поток (ООС)?





вот что написал коллега:





технически, если бы он был реализован правильно, закрывая самый внешний
потока (ООС) должно быть достаточно. Но реализация кажется недостатки.



пример:
BufferedOutputStream наследует close () от FilterOutputStream, который определяет его как:



 155       public void close() throws IOException {
156 try {
157 flush();
158 } catch (IOException ignored) {
159 }
160 out.close();
161 }


однако, если flush () по какой-то причине вызывает исключение времени выполнения, то
из.close () никогда не будет вызван. Так что это кажется "безопасным" (но уродливым)
в основном беспокоиться о закрытии FOS, который держит файл открытым.





что считается самым лучшим, когда-вам-абсолютно-нужно-быть-уверенным, подход к закрытию вложенных ручьи?



и есть ли какие-либо официальные документы Java/Sun, которые занимаются этим в мельчайших деталях?

330   10  

10 ответов:

Я обычно делаю следующее. Во-первых, определите класс на основе шаблона-метода для работы с try/catch mess

import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public abstract class AutoFileCloser {
    // the core action code that the implementer wants to run
    protected abstract void doWork() throws Throwable;

    // track a list of closeable thingies to close when finished
    private List<Closeable> closeables_ = new LinkedList<Closeable>();

    // give the implementer a way to track things to close
    // assumes this is called in order for nested closeables,
    // inner-most to outer-most
    protected final <T extends Closeable> T autoClose(T closeable) {
            closeables_.add(0, closeable);
            return closeable;
    }

    public AutoFileCloser() {
        // a variable to track a "meaningful" exception, in case
        // a close() throws an exception
        Throwable pending = null;

        try {
            doWork(); // do the real work

        } catch (Throwable throwable) {
            pending = throwable;

        } finally {
            // close the watched streams
            for (Closeable closeable : closeables_) {
                if (closeable != null) {
                    try {
                        closeable.close();
                    } catch (Throwable throwable) {
                        if (pending == null) {
                            pending = throwable;
                        }
                    }
                }
            }

            // if we had a pending exception, rethrow it
            // this is necessary b/c the close can throw an
            // exception, which would remove the pending
            // status of any exception thrown in the try block
            if (pending != null) {
                if (pending instanceof RuntimeException) {
                    throw (RuntimeException) pending;
                } else {
                    throw new RuntimeException(pending);
                }
            }
        }
    }
}

обратите внимание на" ожидающее " исключение - это заботится о случае, когда исключение, вызванное во время закрытия, будет маскировать исключение, о котором мы действительно могли бы заботиться.

наконец-то пытается закрыть снаружи любого украшенного потока сначала, поэтому, если у вас был BufferedWriter, обертывающий FileWriter, мы сначала пытаемся закрыть BuffereredWriter, и если это не удается, все равно попробуйте закрыть сам файловый редактор. (Обратите внимание, что определение Closeable вызывает close (), чтобы игнорировать вызов, если поток уже закрыт)

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

try {
    // ...

    new AutoFileCloser() {
        @Override protected void doWork() throws Throwable {
            // declare variables for the readers and "watch" them
            FileReader fileReader = 
                    autoClose(fileReader = new FileReader("somefile"));
            BufferedReader bufferedReader = 
                    autoClose(bufferedReader = new BufferedReader(fileReader));

            // ... do something with bufferedReader

            // if you need more than one reader or writer
            FileWriter fileWriter = 
                    autoClose(fileWriter = new FileWriter("someOtherFile"));
            BufferedWriter bufferedWriter = 
                    autoClose(bufferedWriter = new BufferedWriter(fileWriter));

            // ... do something with bufferedWriter
        }
    };

    // .. other logic, maybe more AutoFileClosers

} catch (RuntimeException e) {
    // report or log the exception
}

используя этот подход вам никогда не придется беспокоиться о try/catch/finally, чтобы иметь дело с закрытием файлов снова.

Если это слишком тяжело для вашего использования, по крайней мере, подумайте о том, чтобы следовать try/catch и "ожидающему" переменному подходу, который он использует.

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

смотрите Потоки Ввода-Вывода Java для сведения.

чтобы решить эту проблему

однако, если flush () по какой-то причине выбрасывает исключение времени выполнения, то out.close () никогда не будет вызван.

Это неправильно. После того, как вы поймаете и проигнорируете это исключение, выполнение будет забрать обратно после блока catch и out.close() оператор будет выполнен.

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

в эпоху Java 7,try-with-resources это, конечно, путь. Как упоминалось в нескольких предыдущих ответах, запрос закрытия распространяется от самого внешнего потока к самому внутреннему потоку. Таким образом, единственное закрытие-это все, что требуется.

try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
  // do something with ois
}

однако есть проблема с этим шаблоном. Try-with-resources не знает о внутреннем FileInputStream, поэтому если конструктор ObjectInputStream создает исключение, FileInputStream никогда не закрывается (пока сборщик мусора не доберется до него). Решение...

try (FileInputStream fis = new FileInputStream(f); ObjectInputStream ois = new ObjectInputStream(fis)) {
  // do something with ois
}

это не так элегантно, но более надежно. Так ли это на самом деле проблема будет зависеть от того, какие исключения могут быть брошены во время строительства внешний объект(ы). ObjectInputStream может бросить IOException, который вполне может быть обработан приложением без завершения. Многие потоковые классы только выбрасывают непроверенные исключения, что вполне может привести к завершению работы приложения.

рекомендуется использовать Apache Commons для обработки объектов, связанных с IO.

на finally предложение use IOUtils

IOUtils.closeQuietly(bWriter); IOUtils.closeQuietly (oWritter);

фрагмент кода ниже.

BufferedWriter bWriter = null;
OutputStreamWriter oWritter = null;

try {
  oWritter  = new OutputStreamWriter( httpConnection.getOutputStream(), "utf-8" );
  bWriter = new BufferedWriter( oWritter );
  bWriter.write( xml );
}
finally {
  IOUtils.closeQuietly(bWriter);
  IOUtils.closeQuietly(oWritter);
}

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

лично я бы игнорировал RuntimeException, потому что непроверенное исключение означает ошибку в программе. Если программа неверна, исправьте ее. Вы не можете "обрабатывать" плохую программу во время выполнения.

это удивительно неудобный вопрос. (Даже если предположить acquire; try { use; } finally { release; } код правильный.)

если конструкция декоратора терпит неудачу, то вы не будете закрывать основной поток. Поэтому вам нужно явно закрыть базовый поток, будь то в finally after use или, более diifcult после успешной передачи ресурса декоратору).

если исключение вызывает сбой выполнения, вы действительно хотите сбросить?

некоторые декораторы на самом деле имеют ресурсы сами. Текущая реализация ВС ZipInputStream например, выделена память кучи не Java.

было заявлено, что (IIRC) две трети ресурсов, используемых в библиотеке Java, реализованы явно неверным образом.

пока BufferedOutputStream закрывается даже на IOException С flush,BufferedWriter правильно закрывается.

мой совет: закройте ресурсы как можно быстрее, и не позволяйте им портить другим кодом. Ото, вы можете потратить слишком много времени на этот вопрос - если OutOfMemoryError бросается приятно вести себя красиво, но другие аспекты вашей программы, вероятно, имеют более высокий приоритет, и код библиотеки, вероятно, сломан в этой ситуации в любом случае. Но я всегда писал:

final FileOutputStream rawOut = new FileOutputStream(file);
try {
    OutputStream out = new BufferedOutputStream(rawOut);
    ... write stuff out ...
    out.flush();
} finally {
    rawOut.close();
}

(Смотри: нет улова!)

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

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

к сожалению, для разработки Android этот сладкий становится доступным только с помощью Android Studio (я думаю) и таргетинг Kitkat и выше.

Также вам не нужно закрывать все вложенные потоки

проверить это http://ckarthik17.blogspot.com/2011/02/closing-nested-streams.html

Я использую, чтобы закрыть потоки, как это, без вложенности попробуйте-поймайте в конце концов блоки

public class StreamTest {

public static void main(String[] args) {

    FileOutputStream fos = null;
    BufferedOutputStream bos = null;
    ObjectOutputStream oos = null;

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

JavaDocs Солнца включают RuntimeExceptions в их документации, как показано InputStream read (byte [], int, int) метод; документировано как бросание NullPointerException и IndexOutOfBoundsException.

FilterOutputStream в flush () это только документально, как бросать исключение IOException, таким образом, он фактически не бросайте RuntimeExceptions. любой, который может быть брошен, скорее всего, будет завернут в IIOException.

Она все еще может бросить Error, но вы не так много можете сделать с ними; Sun рекомендует вам не пытаться их поймать.

Comments

    Ничего не найдено.