В Java, когда я вызываю OutputStream.close (), всегда ли мне нужно вызывать OutputStream.flush () раньше? - PullRequest
40 голосов
/ 28 апреля 2010

Если я просто вызываю close() в выходном потоке, вывод гарантирован, или мне нужно всегда звонить flush()?

Ответы [ 4 ]

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

Хотя close должен вызывать flush, это немного сложнее, чем это ...

Во-первых, декораторы (такие как BufferedOutputStream) распространены в Java. Построение декоратора может быть неудачным, поэтому вам нужно close «сырой» поток в блоке finally, в try которого включен декоратор. В случае исключения обычно не требуется close декоратор (за исключением, например, плохо реализованных декораторов сжатия). Обычно вам нужно flush для декоратора в случае исключения. Поэтому:

final RawOutputStream rawOut = new RawOutputStream(rawThing);
try {
    final DecoratedOutputStream out = new DecoratedOutputStream(rawOut);
    // ... stuff with out within ...
    out.flush();
} finally {
    rawOut.close();
}

Кроме того, методы декоратора close часто реализуются неправильно. Это включает в себя некоторые из java.io до недавнего времени.

Конечно, вы, вероятно, захотите использовать идиому «Выполнить вокруг», чтобы сохранить СУХОЙ (ish).

22 голосов
/ 28 апреля 2010

Close () всегда сбрасывается, поэтому не нужно звонить.

РЕДАКТИРОВАТЬ: Этот ответ основан на здравом смысле и всех выходных потоков, с которыми я столкнулся. Кто собирается реализовать close () для буферизованного потока без предварительной очистки буфера? Нет смысла вызывать flush перед закрытием (). Однако есть последствия, если flush () вызывается чрезмерно. Это может победить под механизмом буферизации.

4 голосов
/ 12 августа 2016

Здесь так много опасных ответов и комментариев. Продолжайте читать, почему я использовал слово опасный .

Перво-наперво. Проверьте это.

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

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

flush() имеет свои цели.

// client
// sends exactly 234 bytes
// and returns exactly 124 bytes from the server
static byte[] sendAndReceive(final OutputStream output,
                             final InputStream input)
    throws IOException {
    final byte[] request = new byte[234];
    output.write(request);
    // output.flush(); // @@? is this required or not?
    final byte[] response = new byte[124];
    new DataInputStream(input).readFully(response);
    return response;
}

// server
// recieve exactly 234 bytes from the client
// sends exactly 124 bytes
static void receiveAndSend(final InputStream input,
                           final OutputStream output)
    throws IOException {
    final byte[] request = new byte[234];
    new DataInputStream(input).readFully(request);
    final byte[] response = new byte[124];
    output.write(response);
    // output.flush(); // @@? is this required or not?
}

Все могло измениться, но я испытал это на себе около десяти лет назад. Вышеупомянутый исходный код (клиент) работал с Windows XP и не работал с Windows 2000 Server для той же конечной точки (сервера).

И (вы) не должны (не должны) полагаться на поведение, специфичное для реализации close().

static void writeFile(File file, byte[] bytes) throws IOException {
    try (OutputStream out = new FileOutputStream(bytes)) {
        out.write(bytes);
        out.flush(); // who cares what FileInputStream#close does?
    }
}

Также обратите внимание, что flush() не означает запись / отправку ваших данных на физический диск или удаленную конечную точку. В основном он просто сбрасывает буферизованные данные в JVM в базовую ОС.


опечаток

Writer#close() прямо говорит, что это

закрывает поток, сначала сбрасывая его.

Но это не значит, что все подклассы сохраняют этот корневой контракт. См. PrintWriter#close(), который (без flush()) закрывает внутренний out (Writer), который, опять же, зависит от реализации out close().

3 голосов
/ 26 апреля 2016

Если вы хотите сбросить поток, то yes , позвоните flush() перед вызовом close().

Несмотря на все другие ответы об обратном (но, как правильно отмечено в некоторых комментариях), реализация по умолчанию java.io.OutputStream::close() не вызывает flush(). На самом деле это ничего не делает. Если у вас есть исходный код, вы можете легко проверить его сами, в противном случае просто доверьтесь официальному javadoc , указанному здесь:

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

Метод close OutputStream ничего не делает.

Независимо от того, сбрасывается ли close() или нет, самый безопасный способ - это промывать его вручную. Если это снова вспыхнет, кого это волнует?

Ответ "Tom Hawtin - tackline" содержит дополнительную информацию о безопасном закрытии потоков (но на самом деле не дает четкого ответа на первоначальный вопрос = P).

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