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

Java Writer.close: синтаксис и применение
Раздел: Потоки ввода-вывода (Streams) символьные
Writer.close: void

Описание Writer.close

Метод Writer.close() определён в абстрактном классе java.io.Writer и реализует интерфейсы Closeable и AutoCloseable. Он предназначен для закрытия потока записи, освобождения связанных с ним системных ресурсов (например, файловых дескрипторов). После вызова close() дальнейшая запись в этот Writer становится невозможной и приводит к исключению IOException с сообщением о том, что поток закрыт.

Метод не принимает аргументов и возвращает void. При выполнении он может выбросить IOException, если произошла ошибка ввода-вывода (например, не удалось сбросить буфер). Согласно контракту, метод должен гарантировать предварительный вызов flush() перед физическим закрытием. Рекомендуется вызывать close() один раз; повторные вызовы (на одной и той же реализации) обычно игнорируются, однако это поведение не гарантируется и может различаться. Наиболее безопасный способ использования – конструкция try-with-resources (Java 7+), где закрытие происходит автоматически.

Примеры использования

Закрытие FileWriter через try-with-resources

try (Writer writer = new FileWriter("file.txt")) {
    writer.write("Пример текста");
} // writer.close() вызывается автоматически
System.out.println("Файл успешно записан и закрыт");
Файл успешно записан и закрыт

Явный вызов close() с обработкой ошибок

Writer writer = null;
try {
    writer = new BufferedWriter(new FileWriter("output.txt"));
    writer.write("Строка данных");
} catch (IOException e) {
    System.err.println("Ошибка записи: " + e.getMessage());
} finally {
    if (writer != null) {
        try {
            writer.close();
        } catch (IOException e) {
            System.err.println("Ошибка при закрытии: " + e.getMessage());
        }
    }
}
(при отсутствии ошибок ничего не выводится)

Закрытие PrintWriter, который оборачивает другой Writer

PrintWriter pw = new PrintWriter(new FileWriter("log.txt"));
pw.println("Лог-сообщение");
pw.close();
System.out.println("PrintWriter закрыт, нижележащий FileWriter также закрыт");
PrintWriter закрыт, нижележащий FileWriter также закрыт

Закрытие StringWriter (закрытие виртуальное)

StringWriter sw = new StringWriter();
sw.write("внутренний буфер");
sw.close(); // не освобождает реальные ресурсы, только устанавливает флаг закрытости
System.out.println("StringWriter закрыт, буфер доступен: " + sw.toString());
StringWriter закрыт, буфер доступен: внутренний буфер

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

Аналогичное назначение имеют методы:

  • OutputStream.close() – закрытие потока вывода байтов, также бросает IOException.
  • Reader.close() – закрытие потока чтения символов.
  • InputStream.close() – закрытие потока чтения байтов.
  • AutoCloseable.close() – базовый интерфейс для ресурсов, поддерживающих try-with-resources.

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

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

PHP

Функция fclose($handle) закрывает файловый указатель. Возвращает bool (true/false), не выбрасывает исключений.

$f = fopen('file.txt', 'w');
fwrite($f, 'data');
fclose($f); // возвращает true при успехе
true

JavaScript (Node.js)

Для файлов используется асинхронный fs.close(fd, callback) или синхронный fs.closeSync(fd). Нет встроенного Writer для символов – работа с буферами.

const fs = require('fs');
const fd = fs.openSync('file.txt', 'w');
fs.writeSync(fd, 'data');
fs.closeSync(fd);
console.log('Файл закрыт');
Файл закрыт

Python

Контекстный менеджер with open() as f: автоматически закрывает файл. Ручной вызов f.close() также возможен.

with open('file.txt', 'w') as f:
    f.write('data')
# f.close() вызывается автоматически
print('Файл закрыт')
Файл закрыт

C#

Метод StreamWriter.Close() или конструкция using. Бросает исключения при ошибках.

using (var writer = new StreamWriter("file.txt")) {
    writer.Write("data");
} // writer.Dispose() вызывает Close()
(без вывода)

Go

Метод (*os.File).Close() возвращает ошибку. Часто используется с defer.

f, _ := os.Create("file.txt")
f.WriteString("data")
err := f.Close()
if err != nil {
    log.Fatal(err)
}
(без вывода)

Основное отличие от Java: в Java close() обязательно вызывает flush() и может выбросить IOException, которое нужно обрабатывать. В других языках (PHP, Python) закрытие реже вызывает исключения, а в Go и Node.js ошибка возвращается явно.

Типичные ошибки

Запись после закрытия

Writer w = new FileWriter("test.txt");
w.close();
w.write("попытка записи"); // IOException
Exception in thread "main" java.io.IOException: Stream closed

Забыли закрыть поток (утечка ресурсов)

Writer w = new FileWriter("log.txt");
w.write("данные");
// w.close() не вызван – файловый дескриптор не освобождён
(файл остаётся открытым до завершения программы или сборки мусора)

Повторный close() не всегда безопасен

Writer w = new FileWriter("test.txt");
w.close();
w.close(); // некоторые реализации бросают IOException
(зависит от реализации; в FileWriter повторный close может вызвать исключение)

Исключение при close() маскирует основную ошибку

try (Writer w = new FileWriter("/root/secret.txt")) {
    w.write("текст");
} catch (IOException e) {
    // если ошибка возникла при write() и потом при close(), перехватывается только первая?
    // на самом деле, при использовании try-with-resources, исключение при close() подавляется, если уже есть исключение из блока try
}
(механизм подавленных исключений)

Рекомендации: всегда использовать try-with-resources или тщательно обрабатывать close() в finally. Проверять на null перед вызовом. Не полагаться на идемпотентность повторного close().

Изменения в версиях

Метод close() присутствует в Java начиная с версии 1.1. Основные вехи:

  • Java 1.1 – введение java.io.Writer с методом close().
  • Java 5.0 – интерфейс Closeable (расширяет AutoCloseable).
  • Java 7 – появление интерфейса AutoCloseable и конструкции try-with-resources, что сделало ручной вызов close() менее необходимым. Реализации Writer начали корректно поддерживать подавленные исключения.
  • Java 9+ – поведение не изменилось, хотя появились новые классы (например, java.io.StringWriter уже существовал).

Критических изменений сигнатуры или семантики метода не происходило. Однако реализации могут различаться в обработке повторного вызова close().

Расширенные примеры

Закрытие цепочки обёрнутых Writer

При использовании BufferedWriter, оборачивающего FileWriter, достаточно закрыть только внешний поток. Закрытие внешнего вызовет flush() и close() внутреннего.

Пример java
try (Writer writer = new BufferedWriter(new FileWriter("output.txt"))) {
    writer.write("Длинный текст, который буферизуется");
}
// автоматически сбрасывается буфер и закрывается FileWriter
(файл записан корректно)

Обработка исключения при close() без потери основной ошибки

Вручную: сохранять исключение из try и добавлять подавленные из finally.

Пример java
Writer w = null;
IOException mainEx = null;
try {
    w = new FileWriter("file.txt");
    w.write("данные");
} catch (IOException e) {
    mainEx = e;
} finally {
    if (w != null) {
        try {
            w.close();
        } catch (IOException e) {
            if (mainEx != null) {
                mainEx.addSuppressed(e);
            } else {
                mainEx = e;
            }
        }
    }
    if (mainEx != null) {
        // пробросить или залогировать
        mainEx.printStackTrace();
    }
}
(стек ошибок с подавленными)

Использование close() с CharArrayWriter (виртуальное закрытие)

CharArrayWriter.close() не освобождает ресурсы, но помечает поток как закрытый. Буфер остаётся доступен для toString().

Пример java
CharArrayWriter caw = new CharArrayWriter();
caw.write("12345");
caw.close();
// даже после close() можно получить данные
System.out.println("Буфер после закрытия: " + caw.toString());
// но запись после close() вызовет IOException
caw.write("доп"); // java.io.IOException: Stream closed
Буфер после закрытия: 12345
Exception in thread "main" java.io.IOException: Stream closed

Многопоточное закрытие – опасность состояния гонки

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

Пример java
Writer w = new FileWriter("shared.txt");
Thread writerThread = new Thread(() -> {
    try {
        w.write("поток 1");
        Thread.sleep(100);
    } catch (Exception e) {}
});
Thread closerThread = new Thread(() -> {
    try {
        Thread.sleep(50);
        w.close(); // может вызвать исключение в writerThread
    } catch (Exception e) {}
});
writerThread.start();
closerThread.start();
(в writerThread может возникнуть IOException: Stream closed)

Закрытие OutputStreamWriter, обёрнутого в BufferedWriter

Пример java
try (Writer writer = new BufferedWriter(
        new OutputStreamWriter(
            new FileOutputStream("data.txt"), "UTF-8"))) {
    writer.write("Юникод-текст");
} // последовательно закрываются BufferedWriter, OutputStreamWriter, FileOutputStream
(файл записан в кодировке UTF-8)

джава Writer.close function comments

En
Writer.close Closes the stream, flushing it first