Буферизированный ввод и вывод
Зачем нужен ещё один слой вокруг потока
Частое обращение к файлу или другому внешнему источнику может быть заметно дороже, чем работа с памятью. Буферизированные потоки уменьшают количество таких мелких операций: они читают или накапливают данные блоками во внутреннем буфере, а код пользователя работает с привычным API.
Для байтов есть BufferedInputStream и BufferedOutputStream,
для символов — BufferedReader и BufferedWriter.
Обычно они оборачивают уже существующий поток или reader/writer.
try (InputStream in = new BufferedInputStream(
new FileInputStream("photo.jpg"))) {
byte[] header = in.readNBytes(16);
}
Снаружи это всё ещё InputStream, поэтому остальной код может не знать,
есть внутри буфер или нет. Это хороший пример композиции потоков: дополнительные возможности добавляются обёрткой.
BufferedReader и чтение строк
Для текста буферизация особенно заметна благодаря BufferedReader:
он умеет читать данные построчно через readLine().
Возвращённая строка не содержит символ перевода строки, а null означает конец данных.
try (BufferedReader reader = new BufferedReader(
new FileReader("app.log", StandardCharsets.UTF_8))) {
String line = reader.readLine();
System.out.println(line);
}
Когда нужен flush()
Буферизированный вывод может некоторое время держать записанные данные в памяти.
Метод flush() просит передать накопленные данные дальше по цепочке,
не закрывая сам поток. Это важно, когда другой участник должен увидеть вывод до завершения работы ресурса.
try (BufferedWriter writer = new BufferedWriter(
new FileWriter("status.txt", StandardCharsets.UTF_8))) {
writer.write("READY");
writer.flush();
}