OutputStream.close: примеры (JAVA)

Закрытие потоков через метод close
Раздел: Потоки ввода-вывода (Streams) байтовые
OutputStream.close: void

Описание и назначение

Метод OutputStream.close() служит для закрытия потока вывода и освобождения связанных с ним системных ресурсов. После вызова close() дальнейшие операции записи обычно приводят к IOException. Реализация может выполнить дополнительную очистку, например вызвать flush() перед закрытием.

Сигнатура и возвращаемое значение

Сигнатура в классе java.io.OutputStream:

public void close() throws IOException

Метод не возвращает значений (void). При ошибке ввода-вывода выбрасывается IOException. Многие подклассы переопределяют поведение.

Когда используется

  • После завершения записи в файл, сокет или другой носитель данных.
  • Для освобождения системных дескрипторов и буферов.
  • В сочетании с try-with-resources для автоматического закрытия.

Особенности реализации

  • В стандартной реализации FilterOutputStream.close() сначала вызывает flush(), затем закрывает вложенный поток.
  • Некоторые реализации могут игнорировать повторные вызовы close() (идемпотентность), другие могут выбрасывать исключение при операциях после закрытия.
  • Класс OutputStream реализует интерфейс Closeable, что позволяет использовать try-with-resources (Java 7+).

Короткие примеры использования

1. Ручное закрытие в try-finally

FileOutputStream fos = null;
try {
    fos = new FileOutputStream("out.txt");
    fos.write("Привет\n".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fos != null) {
        try { fos.close(); } catch (IOException ignored) {}
    }
}
Код создаст файл out.txt с содержимым "Привет". При ошибке будет напечатан стек трейс, затем поток закроется.

2. try-with-resources (рекомендуется)

try (FileOutputStream fos = new FileOutputStream("out2.txt")) {
    fos.write("Тест\n".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
    e.printStackTrace();
}
Файл out2.txt будет содержать "Тест". Поток закроется автоматически, исключения закрытия станут подавленными и доступны через Throwable.getSuppressed().

3. Закрытие обёртки и влияние на внутренний поток

try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("out3.txt"))) {
    bos.write(new byte[]{1,2,3});
} catch (IOException e) {
    e.printStackTrace();
}
BufferedOutputStream.flush() будет вызван перед закрытием, данные попадут в файл out3.txt.

Похожие методы и их особенности в Java

  • flush() - очищает буферы, не закрывая поток. Используется когда нужно гарантировать запись, но продолжить работу с потоком.
  • Closeable.close() и AutoCloseable.close() - интерфейсы, которые реализуют потоки; различие в сигнатурах (AutoCloseable.close() может бросать Exception, Closeable.close() бросает IOException).
  • Socket.shutdownOutput() - для сокетов сигнализирует о завершении отправки данных, но сам сокет остаётся открыным для чтения.
  • Channel.close() (NIO) - закрывает канал; при работе с FileChannel предпочтительнее использовать NIO-методы.

При необходимости только сброса буферов лучше применять flush(). Для автоматического управления ресурсами предпочтительнее try-with-resources с Closeable.

Аналоги в других языках и отличия

  • PHP
  • $fp = fopen('out.txt', 'w');
    fwrite($fp, "Пример\n");
    fclose($fp);
    fclose закрывает ресурс; в PHP ошибки закрытия обычно не выбрасываются как исключения.
  • JavaScript (Node.js)
  • const fs = require('fs');
    const stream = fs.createWriteStream('out.txt');
    stream.write('Hi\n');
    stream.end(); // закрывает и вызывает finish
    end() завершает запись и закрывает поток; ошибки передаются через событие 'error'.
  • Python
  • with open('out.txt', 'wb') as f:
        f.write(b'Привет\n')
    Контекстный менеджер автоматически вызывает close(); исключения закрытия могут подавляться аналогично Java.
  • C#
  • using (var fs = new FileStream("out.txt", FileMode.Create)) {
        var data = System.Text.Encoding.UTF8.GetBytes("Привет\n");
        fs.Write(data, 0, data.Length);
    }
    using гарантирует вызов Dispose(), который выполняет закрытие потока. Исключения различаются: IOException и другие типы.
  • Go
  • f, _ := os.Create("out.txt")
    defer f.Close()
    f.Write([]byte("Привет\n"))
    Close возвращает ошибку, которую рекомендуется проверять; defer обеспечивает вызов Close.
  • Lua
  • local f = io.open('out.txt', 'w')
    f:write('Привет\n')
    f:close()
    close возвращает true/false и сообщение об ошибке; модель отличается от исключений Java.
  • Kotlin
  • val fos = FileOutputStream("out.txt")
    fos.use { it.write("Привет\n".toByteArray()) }
    use является обёрткой над try-with-resources, автоматически закрывает поток.

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

Повторное поле ошибок не добавлено (см. раздел errors выше).

Изменения в поведении в новых версиях Java

  • Сама сигнатура OutputStream.close() практически не менялась в последних релизах; метод остаётся наследуемым и бросает IOException.
  • Ключевое изменение - введение try-with-resources в Java 7 и интерфейса AutoCloseable, что упростило корректное закрытие потоков. Closeable совместим с AutoCloseable, и это повлияло на паттерны использования.
  • В последних релизах JDK улучшалось поведение средств диагностики (jcmd, утилиты) по утечкам дескрипторов, но сам метод остаётся обратнопсовместимым.

Продвинутые и необычные примеры

1. Обработка подавленных исключений при try-with-resources

Пример java
class BadStream extends OutputStream {
    private final boolean throwOnClose;
    BadStream(boolean t) { this.throwOnClose = t; }
    public void write(int b) {}
    public void close() throws IOException {
        if (throwOnClose) throw new IOException("close failed");
    }
}

try (BadStream s = new BadStream(true)) {
    throw new IOException("write failed");
} catch (IOException e) {
    System.out.println("Caught: " + e.getMessage());
    for (Throwable t : e.getSuppressed()) System.out.println("Suppressed: " + t.getMessage());
}
Вывод:
Caught: write failed
Suppressed: close failed
Появляется подавленное исключение от close, доступное через getSuppressed().

2. Создание потока, который не закрывает внутренний ресурс (CloseShield)

Пример java
class NoCloseOutputStream extends FilterOutputStream {
    NoCloseOutputStream(OutputStream out) { super(out); }
    public void close() throws IOException { // переопределено: ничего не закрывать
        out.flush(); // можно только сбросить буфер
    }
}

FileOutputStream fos = new FileOutputStream("shared.txt");
try (NoCloseOutputStream nc = new NoCloseOutputStream(fos)) {
    nc.write("A\n".getBytes());
}
fos.write("B\n".getBytes());
fos.close();
Используется когда один ресурс должен обслуживать несколько обёрток, и закрытие одной из них не должно закрывать общий ресурс.

3. Закрытие socket.getOutputStream() vs socket.close()

Пример java
Socket socket = new Socket(host, port);
OutputStream os = socket.getOutputStream();
os.write(...);
os.close(); // закрывает только поток вывода на стороне сокета
// для полного освобождения TCP-соединения рекомендуется socket.close()
shutdownOutput/signals могут отличаться: закрытие только OutputStream отправит FIN для отправки, но входящий поток остаётся слушаемым до socket.close().

4. Переопределение close в собственном потоке с учётом конкурентного доступа

Пример java
class SafeOutputStream extends OutputStream {
    private volatile boolean closed = false;
    public synchronized void write(int b) throws IOException {
        if (closed) throw new IOException("closed");
        // запись
    }
    public synchronized void close() throws IOException {
        if (closed) return;
        // очистка ресурсов
        closed = true;
    }
}
Используется для предотвращения гонок при одновременных вызовах write/close из разных потоков.

5. Применение Closeable при работе с NIO каналом

Пример java
try (WritableByteChannel ch = Files.newByteChannel(Paths.get("out.bin"), StandardOpenOption.CREATE)) {
    ch.write(ByteBuffer.wrap(new byte[]{1,2,3}));
} catch (IOException e) {
    e.printStackTrace();
}
Channel закроется автоматически; предпочтительно для высокопроизводительных операций ввода-вывода.

джава OutputStream.close function comments

En
OutputStream.close Closes this output stream and releases any system resources associated with this stream