はじめに
こんにちは。
スッキリわかるJava入門 実践編の学習を進めています。
この学習記録を備忘録として残しながら、自身の理解を深めたいと思っています。
今回は第2部 / 第6章「ファイルの操作」について学びました。
Javaにおけるストリームの基礎知識から、try-with-resources による安全なファイル入出力、そして Path/Files クラスを使った直感的なファイル操作までを学びました。
この記事が、同じくJavaの学習をしている人にとっても参考になりましたら幸いです。
参考書籍について
- 書籍名:スッキリわかるJava入門 実践編
- 著者:中山清喬
- 出版社:インプレス
目次
1. 本章の要点
ストリームの概念
ストリームとは、データの伝送路を表す概念で、JVMはストリームを通じてファイル、ネットワーク、キーボードなどのデータソースとやり取りします。
ファイル操作で主に使うストリームは、2つの種類に分けられます
- 文字ストリーム:文字(Char)のデータが流れる。テキストファイルに最適。使用クラス:FileReader、FileWriter
- バイトストリーム:バイト(byte)のデータが流れる。バイナリファイルや汎用的に使用。使用クラス:FileInputStream、FileOutputStream
ファイル操作の前提知識
ファイル操作は、「ファイルを開く」 → 「データを読み書きする」 → 「ファイルを閉じる」という3つのステップで構成されます。
Javaで扱うファイルには2つの種類があります
- テキストファイル:人間が読める文字データで構成(.txt, .csv など)
- バイナリファイル:バイト単位のデータで構成(.jpg, .exe など)
テキストファイルの読み書き
import java.io.*;
public class TextFileExample {
// ファイルに書き込む
public static void writeTextFile(String filename, String content) throws IOException {
FileWriter writer = new FileWriter(filename);
try {
writer.write(content);
System.out.println("ファイルに書き込みました:" + filename);
} finally {
writer.close();
}
}
// ファイルから読み込む
public static void readTextFile(String filename) throws IOException {
FileReader reader = new FileReader(filename);
try {
int ch;
StringBuilder sb = new StringBuilder();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
System.out.println("ファイル内容:");
System.out.println(sb.toString());
} finally {
reader.close();
}
}
public static void main(String[] args) throws IOException {
// テキストファイルに書き込む
String content = "ミナト\nアサカ\nシェナ";
writeTextFile("heroes.txt", content);
// テキストファイルから読み込む
readTextFile("heroes.txt");
}
}
バイナリファイルの読み書き
import java.io.*;
public class BinaryFileExample {
// バイナリファイルに書き込む
public static void writeBinaryFile(String filename, byte[] data) throws IOException {
FileOutputStream stream = new FileOutputStream(filename);
try {
stream.write(data);
System.out.println("バイナリファイルに書き込みました:" + filename);
} finally {
stream.close();
}
}
// バイナリファイルから読み込む
public static void readBinaryFile(String filename) throws IOException {
FileInputStream stream = new FileInputStream(filename);
try {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = stream.read(buffer)) != -1) {
System.out.println("読み込んだバイト数:" + bytesRead);
}
} finally {
stream.close();
}
}
public static void main(String[] args) throws IOException {
// バイナリデータを作成
byte[] data = new byte[] {
0x89, 0x50, 0x4E, 0x47 // PNGファイルの識別子の例
};
// バイナリファイルに書き込む
writeBinaryFile("sample.bin", data);
// バイナリファイルから読み込む
readBinaryFile("sample.bin");
}
}
try-with-resourcesによる安全な例外処理
ファイルは必ずクローズしないといけません。try-with-resourcesを使うことで、例外が発生してもファイルが確実にクローズされます。
import java.io.*;
import java.nio.file.*;
public class SafeFileHandling {
// try-with-resources文
public static void safeWrite(String filename, String content) throws IOException {
try (FileWriter writer = new FileWriter(filename)) {
writer.write(content);
System.out.println("安全に書き込みました");
} // ここで自動的にクローズされる
}
// try-with-resources文で複数のリソースを扱う
public static void safeReadAndWrite(String inputFile, String outputFile) throws IOException {
try (FileReader reader = new FileReader(inputFile);
FileWriter writer = new FileWriter(outputFile)) {
int ch;
while ((ch = reader.read()) != -1) {
writer.write(ch);
}
System.out.println("ファイルをコピーしました");
} // 両方のファイルが自動的にクローズされる
}
public static void main(String[] args) throws IOException {
safeWrite("test.txt", "テスト内容");
safeReadAndWrite("test.txt", "copy.txt");
}
}
ファイルクローズが重要な理由
ファイルを閉じないと、以下の問題が発生します
- メモリリーク(ファイルディスクリプタが消費される)
- ファイルのロック(他のプロセスが操作できない)
- データ喪失(バッファがフラッシュされない)
ファイルシステムの操作
java.io.Fileクラスは基本的なファイル操作ができますが、java.nio.FileとPath/Filesクラスでより高度で安全なファイルシステム操作が実現できます。
import java.io.*;
import java.nio.file.*;
public class FileSystemOperations {
// 従来のFileクラス
public static void legacyFileClass() {
File file = new File("data.txt");
// ファイルの存在確認
System.out.println("存在するか:" + file.exists());
// ファイルのサイズ
System.out.println("ファイルサイズ:" + file.length() + " バイト");
// ファイルの削除
if (file.exists()) {
file.delete();
System.out.println("ファイルを削除しました");
}
}
// Path/Filesクラス
public static void modernPathClass() throws IOException {
Path path = Paths.get("data.txt");
// ファイルの存在確認
System.out.println("存在するか:" + Files.exists(path));
// ファイルのサイズ
System.out.println("ファイルサイズ:" + Files.size(path) + " バイト");
// 最終更新日時
System.out.println("最終更新:" + Files.getLastModifiedTime(path));
// ファイル属性の確認
System.out.println("読み取り可能:" + Files.isReadable(path));
System.out.println("書き込み可能:" + Files.isWritable(path));
// ファイルのコピー
Path copyPath = Paths.get("copy.txt");
Files.copy(path, copyPath, StandardCopyOption.REPLACE_EXISTING);
System.out.println("ファイルをコピーしました");
// ファイルの削除
Files.delete(path);
System.out.println("ファイルを削除しました");
}
// ディレクトリ内のファイル一覧
public static void listFiles(String directoryPath) throws IOException {
Path dir = Paths.get(directoryPath);
Files.list(dir)
.forEach(path -> System.out.println(path.getFileName()));
}
public static void main(String[] args) throws IOException {
// テストファイル作成
Files.write(Paths.get("data.txt"), "テストデータ".getBytes());
modernPathClass();
}
}
2. 自分なりの解釈
- ストリームの概念は、ファイルだけでなくネットワークやキーボード入力など、様々なデータソースに統一的にアクセスするための重要な仕組み。
- try-with-resourcesの登場により、ファイルのクローズ漏れがなくなり、より安全で読みやすいコードが実現できるようになった。
- Path/Filesクラスの導入により、ファイルシステム操作がより直感的で、複雑な操作も簡潔に記述できるようになった。
最後まで見ていただきありがとうございました。
次回は、第2部 / 第7章「さまざまなファイル形式」について書く予定です。