BufferedWriter を一言でいうと
BufferedWriter は、
「文字をいったんバッファ(メモリ)に貯めてから、まとめて出力ストリームに書き込むことで、高速にテキストを書き込むためのクラス」
です。
Writer をラップして使い、文字・文字配列・文字列を効率よく書き出せます。newLine() でプラットフォームに依存した改行も簡単に出せるようになっています。
BufferedWriter の役割と基本イメージ
なぜ「バッファ」が必要なのか
FileWriter などの Writer をそのまま使うと、write() を呼ぶたびに、文字がすぐ下流のストリーム(ファイルなど)へ送られる実装になりがちです。
1 文字や短い文字列を大量に書く場合、
- OS への書き込み回数が非常に多くなる
- ディスク I/O が増えて遅くなる
といった問題が出ます。
そこで BufferedWriter は、
- 内部に「文字のバッファ」を持つ
write()が呼ばれるたびに、まずバッファに貯める- バッファがいっぱいになったり、
flush()/close()が呼ばれたタイミングで、一気に下流の Writer へ送る
という動きをします。
この「まとめ書き」によって、ディスクアクセスや出力ストリームへのアクセス回数が減り、パフォーマンスが大きく改善されます。
「Writer をラップする」クラスであること
BufferedWriter 自体は出力先を持っていません。
常に「別の Writer を包んで」使います。
例えば:
- ファイルへ出力したい →
new BufferedWriter(new FileWriter("foo.txt")) OutputStreamに書きたい →new BufferedWriter(new OutputStreamWriter(out))
というように、「どこに書くか」を担当する Writer を下流に持ち、その上にバッファリング機能をかぶせるイメージです。
基本的な使い方:ファイルにテキストを書く
最小構成のサンプル
ファイルに数行のテキストを書き込む基本例です。
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterBasic {
public static void main(String[] args) {
String fileName = "output.txt";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
writer.write("1行目のテキスト");
writer.newLine(); // 改行を1つ出力
writer.write("2行目のテキスト");
writer.newLine();
writer.write("3行目");
// try-with-resources により、ここを抜けるときに自動で flush + close
} catch (IOException e) {
e.printStackTrace();
}
}
}
Javaここで押さえておくポイントは次の通りです。
FileWriterをBufferedWriterでラップしているwrite(...)で文字列を書き、newLine()で改行を入れている- try-with-resources により、最後に自動で
flush()とclose()が呼ばれる
特に newLine() は、System.getProperty("line.separator")(プラットフォームごとの改行文字列)に従って適切な改行を出力してくれるため、単に "\n" と書くよりも移植性が高いです。
重要メソッド:write / newLine / flush / close
write:文字列・文字配列を書き込む
BufferedWriter は Writer を継承しているので、基本的な write メソッドを持ちます。
代表的なものは:
writer.write(int c); // 1文字
writer.write(char[] cbuf); // 文字配列
writer.write(char[] cbuf, int off, int len);
writer.write(String s); // 文字列
writer.write(String s, int off, int len);
Javawrite(String s) を使う場面が最も多いと思ってよいです。
内部的には、「すぐにディスクに行く」のではなく、「まずバッファに貯める」ことを意識しておくとよいです。
newLine:プラットフォーム依存の改行を出す
newLine() は、BufferedWriter 固有の便利メソッドです。
writer.newLine();
Javaこれにより、
- Windows なら
\r\n - Unix / Linux / macOS なら
\n
など、その環境の line.separator システムプロパティに基づく改行文字列が書き込まれます。
ドキュメントでも、「改行文字を直接書くより、newLine() を使うことが推奨される」とされています。
プラットフォーム間でログやファイルをやり取りするときにも、newLine() を使っておけば安心です。
flush:バッファの内容を強制的に下流へ流す
writer.flush();
Javaバッファに貯まっている文字を、強制的に下の Writer(ファイルなど)へ送ります。
通常、close() を呼べば内部で自動的に flush() されるので、
try-with-resources を使っている限り、明示的に flush() を呼ぶ必要はあまりありません。
ただし、
- 長時間開きっぱなしで、途中経過を確実にディスクに書きたい
- すぐ後に別プロセスがそのファイルを読むことが分かっている
といったケースでは、flush() を適切なタイミングで呼ぶことがあります。
close:リソース解放(必ず try-with-resources で)
close() は、バッファの flush を行ったうえで、下流の Writer を閉じます。
重要なのは、
BufferedWriterを閉じると、その下にあるWriter(例:FileWriter)も一緒に閉じられる- 閉じた後に
write()するとIOExceptionになる
という点です。
現代の Java では、BufferedWriter を使うときは基本的に
try (BufferedWriter writer = new BufferedWriter(new FileWriter("..."))) {
...
}
Javaと書き、try-with-resources に閉じ処理を任せるのが標準的です。
バッファリングの効果をイメージで理解する
バッファなし書き込みとの違い
FileWriter だけを使うと、write("A") するたびに、
文字列がすぐにバイト列に変換され、ファイルへ書き込まれます。
例えば、1 文字ずつ 10 万回 write() した場合、
- 10 万回の I/O 操作が発生
- ディスクアクセスも多く、非常に非効率
となり得ます。
一方、BufferedWriter を挟むと、
- まずメモリ上のバッファに溜め込む
- バッファが満タンになったところで、まとめて下流へ書く
ので、実際の I/O 回数は大幅に減ります。
公式ドキュメントでも、「FileWriter や OutputStreamWriter など、write() のコストが高い Writer は BufferedWriter でラップすることが推奨される」と明記されています。
バッファサイズはどうなるか
BufferedWriter は、コンストラクタでバッファサイズを指定できます。
BufferedWriter writer = new BufferedWriter(new FileWriter("out.txt"), 8192);
サイズを指定しない場合は、適切とされるデフォルト値が使われます。
通常の用途では、デフォルトのままで十分です。
文字コード(エンコーディング)との関係
FileWriter は「プラットフォームのデフォルトエンコーディング」
new FileWriter("out.txt") は、内部で「プラットフォームのデフォルト文字コード」を使って文字をバイトに変換します。
- Windows 日本語環境なら CP932(Shift_JIS 系)
- Linux や macOS なら UTF-8
など、環境によって変わるため、コードだけ見ても何で書いているか分からないという弱点があります。
エンコーディングを明示するには OutputStreamWriter と組み合わせる
UTF-8 で確実に書き出したい場合は、次のようにします。
import java.io.*;
import java.nio.charset.StandardCharsets;
public class BufferedWriterUtf8 {
public static void main(String[] args) {
try (BufferedWriter writer =
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("output.txt"),
StandardCharsets.UTF_8))) {
writer.write("こんにちは UTF-8 の世界");
writer.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
JavaOutputStreamWriter にエンコーディングを渡し、
それをさらに BufferedWriter でラップしています。
まとめると、
- 文字コードを意識しない簡易用途 →
new BufferedWriter(new FileWriter(...)) - 文字コードをきちんと指定したい用途 →
BufferedWriter + OutputStreamWriter + FileOutputStream
という整理になります。
BufferedWriter を使うときに意識したいこと
常に try-with-resources で使う
BufferedWriter は AutoCloseable なので、必ず close() が必要です。
書き忘れを防ぐためにも、try-with-resources で書くのを習慣にしてください。
try (BufferedWriter writer = new BufferedWriter(new FileWriter("out.txt"))) {
// 書き込み処理
}
Javaこれだけで、
- 正常終了時も
- 例外発生時も
必ず flush() → close() が呼ばれます。
「バッファにあるうちはファイルに見えない」ことを意識する
バッファに溜まっている間は、物理ファイルにはまだ書かれていません。
- すぐに別プロセスがそのファイルを読む
- ログをリアルタイムに tail したい
などのケースでは、
- 適宜
flush()を呼ぶ - ある程度まとめて書く設計にする
などを考えた方が良いです。
まとめ:BufferedWriter を自分の中でこう位置づける
BufferedWriter を初心者向けにまとめると、
「別の Writer をラップし、内部バッファに文字を貯めてからまとめて出力することで、高速かつ便利にテキストを書き込めるクラス」
です。
特に意識しておきたい点は:
- 常に何かの Writer(FileWriter / OutputStreamWriter など)をラップして使う
- 内部バッファを使って書き込み回数を減らし、性能を改善する
write(...)とnewLine()が基本コンボで、newLine()は環境依存の改行を出してくれる- 必ず try-with-resources で使い、
flush/closeを自動化する - エンコーディングを意識する場合は
OutputStreamWriterとの組み合わせが基本
