Writer.close: примеры (JAVA)
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("попытка записи"); // IOExceptionException 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() внутреннего.
try (Writer writer = new BufferedWriter(new FileWriter("output.txt"))) {
writer.write("Длинный текст, который буферизуется");
}
// автоматически сбрасывается буфер и закрывается FileWriter(файл записан корректно)
Обработка исключения при close() без потери основной ошибки
Вручную: сохранять исключение из try и добавлять подавленные из finally.
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().
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 из нескольких потоков закрытие из одного потока может прервать запись в другом. Следует синхронизировать доступ.
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
try (Writer writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("data.txt"), "UTF-8"))) {
writer.write("Юникод-текст");
} // последовательно закрываются BufferedWriter, OutputStreamWriter, FileOutputStream(файл записан в кодировке UTF-8)