CodeGym /コース /JAVA 25 SELF /java.io と java.nio の基礎:違いと API の進化

java.io と java.nio の基礎:違いと API の進化

JAVA 25 SELF
レベル 35, レッスン 0
使用可能

1. Java はどのようにファイルを扱うようになったか

Java とファイル:最初はどうだったか

かつて 1996 年、言語の開発者たちは最初のファイル操作の方法としてパッケージ java.io を提案しました。そこには File、FileInputStream、FileOutputStream、Reader、Writer などのクラスが登場しました。これらのクラスにより、ファイルの存在確認、サイズ、更新日時の取得、ディレクトリの操作などが可能になりました。

しかし、初期バージョンによくあるように、すべてが完璧というわけではなく、もっと便利で安全にできる点が多く残っていました。

旧 API(java.io)の制限

  • File はファイルそのものではなく「ショートカット」。 File クラスは中身の読み書きができません。表すのはパスとメタデータです。読み書きには別のストリーム(FileInputStream/FileOutputStream や Reader/Writer)が必要です。
  • パス操作はつらい。 "C:\\Users\\" + user + "\\Documents" のように手作業で連結すると、Windows と Linux 間の移植で壊れがちでした。
  • シンボリックリンク非対応。 旧 API はシンボリックリンクを理解せず、正しく扱えないことがありました。
  • 属性サポートが弱い。 アクセス権、所有者、「隠し」フラグなどの取得が難しい。
  • 大きなファイルに非効率。 古典的なストリームは大容量データに対する便利で高速なパターンを提供せず、非同期 I/O もありませんでした。

java.nio.file の登場(NIO.2、Java 7)

Java 7 で新しいパッケージ java.nio.file(通称 NIO.2)が登場しました。これにより次が導入されました:

  • Path — 現代的なパス抽象(ZIP 内やクラウド・ネットワークのファイルシステム内のパスも含む)。
  • Files — 読み書き・コピー・削除のための静的ユーティリティ。
  • FileSystem — ローカルディスクだけでなく他のファイルシステムも扱える。
  • シンボリックリンク、拡張属性、アクセス権のサポート。
  • 非同期 I/O と、大量データでの性能向上。
  • ディレクトリ操作の容易さと、ストリーム API との良好な連携。

NIO は「New Input/Output」の略で、登場から 10 年以上経った今でも、Java にとっての現代的な標準です。

2. アプローチ比較: java.io と java.nio.file

クラス File(java.io)

  • ファイルまたはディレクトリのパスとそのメタデータを表す。
  • ファイル内容の読み書きはできない。
  • 存在、サイズ、種類(ファイル/ディレクトリ)、絶対パスなどを取得できる。
import java.io.File;

public class ExampleIO {
    public static void main(String[] args) {
        File file = new File("example.txt");
        if (file.exists()) {
            System.out.println("ファイルは存在します!");
            System.out.println("サイズ: " + file.length() + " バイト");
        }
    }
}

クラス Path(java.nio.file)

  • 現代的なパス抽象で、自然に結合や正規化ができる。
  • ローカル、アーカイブ、ネットワークのパスを表現できる。
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ExampleNIOPath {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        if (Files.exists(path)) {
            System.out.println("ファイルが見つかりました!");
            System.out.println("サイズ: " + Files.size(path) + " バイト");
        }
    }
}

クラス Files(java.nio.file)

  • 読み書きを丸ごとまたは部分的に行うための静的メソッド群。
  • ファイルのコピー、削除、移動。
  • ディレクトリの作成/削除。
  • 拡張属性へのアクセス。
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class ExampleNIOFiles {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("example.txt");
        List<String> lines = Files.readAllLines(path);
        for (String line : lines) {
            System.out.println(line);
        }
    }
}

比較表

機能
java.io.File
java.nio.file.Path + Files
パスの表現 はい はい
内容の読み書き いいえ はい(Files 経由)
パス操作 不便 便利(resolve, normalize)
シンボリックリンク いいえ はい
ファイル属性 制限的 拡張
非同期 I/O いいえ はい(NIO.2)
クロスプラットフォーム性 部分的 優秀

3. いつどちらを使うか?

旧 API(java.io):いつ必要?

  • レガシーコードのサポート用。 プロジェクトが Java 7 以前に始まり、至る所で File、FileInputStream、FileOutputStream を使っているなら、互換性を維持する必要があるかもしれません。
  • 新規プロジェクトでは非推奨。 今日「ベテラン」の File を使うのは、VHS テープで動画を見るようなものです。

新しい API(java.nio.file):現代の標準

  • 新規プロジェクトでは常に Path と Files を優先しましょう。
  • より簡単で安全かつ強力で、ストリームやラムダ、try-with-resources との統合も良好です。

クイックリファレンス

  • 読み書き・コピー・削除? — Files 経由。
  • パス操作(結合、正規化)? — Path。
  • サイズ・日時・アクセス権? — Files.getAttribute() などのメソッド。
  • ディレクトリを再帰的に走査? — Files.walk、Files.list。

4. 例:旧 API と新 API のコード比較

例1: ファイルの存在チェック

旧方式:

import java.io.File;

File file = new File("data.txt");
if (file.exists()) {
    System.out.println("ファイルが見つかりました!");
}

新方式:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path path = Paths.get("data.txt");
if (Files.exists(path)) {
    System.out.println("ファイルが見つかりました!");
}

例2: ファイルの全行読み込み

旧方式:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

File file = new File("data.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
reader.close();

新方式:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

Path path = Paths.get("data.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
    System.out.println(line);
}

コメント: 新方式の方が短くて安全。try-with-resources を用いればリソース管理はさらに簡単になります。

例3: 文字列をファイルへ書き込む

旧方式:

import java.io.FileWriter;

FileWriter writer = new FileWriter("output.txt");
writer.write("やあ、ファイル!");
writer.close();

新方式:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path path = Paths.get("output.txt");
Files.write(path, "やあ、ファイル!".getBytes());

5. 役立つポイント

なぜ Java は作り直したのか?

  • 安全性と信頼性。 リソースのクローズやパス操作でのミスが減ります。
  • クロスプラットフォーム。 Windows、Linux、さらには ZIP アーカイブに対しても共通のクラスで扱えます。
  • 拡張の容易さ。 クラウドやネットワークのファイルシステムのサポートを追加しやすい。
  • パフォーマンス。 大きなファイルでの挙動が向上し、非同期操作に対応。
  • 現代の Java スタックとの親和性。 ラムダ、ストリーム、try-with-resources。

旧 API から新 API へ移行するには?

ほぼ常に File を Path に、またその逆に変換できます:

File file = new File("data.txt");
Path path = file.toPath();
File file2 = path.toFile();

古いコードに出会ったら?

  • 慌てないでください。もっと簡単かつモダンにできます。
  • 可能であれば、java.nio.file に書き換えましょう。
  • 難しい場合はブリッジ(toPath()、toFile())を使い、段階的に移行します。

6. 最後の例:ミニアプリ

例: ファイルの存在を確認し、サイズを出力する。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileInfo {
    public static void main(String[] args) {
        Path path = Paths.get("test.txt");
        if (Files.exists(path)) {
            try {
                long size = Files.size(path);
                System.out.println("ファイルが見つかりました。サイズ: " + size + " バイト");
            } catch (Exception e) {
                System.out.println("ファイルサイズ取得時のエラー: " + e.getMessage());
            }
        } else {
            System.out.println("ファイルが見つかりません!");
        }
    }
}

ここで使ったもの:

  • Path — パスの表現。
  • Files.exists() — 存在確認。
  • Files.size() — サイズ取得。

7. ファイル API での典型的なミス

エラー No.1: File が内容を読み書きできると思い込む。 実際には File は単なる「ショートカット」です。読み書きには FileInputStream/FileOutputStream(旧 API)や、Files(新 API)のユーティリティを使いましょう。

エラー No.2: + やスラッシュでパスを手作業で連結する。 これは OS ごとに不具合の原因になります。Path.resolve() や、複数引数の Paths.get(...) を使いましょう。

エラー No.3: ストリームを閉じ忘れる。 旧 API ではリソースリークの典型的な原因です。新 API の多くの操作は Files を通すためストリームを生成しませんし、必要な場面でも try-with-resources を適用しましょう。

エラー No.4: 新規プロジェクトで旧 API を使う。 新しくプロジェクトを始めるなら java.nio.file を選びましょう。java.io はレガシーコードとの互換性が必要なときにのみ意味があります。

1
タスク
JAVA 25 SELF, レベル 35, レッスン 0
ロック未解除
私の秘密の日記はどこ? 🕵️‍♀️
私の秘密の日記はどこ? 🕵️‍♀️
1
タスク
JAVA 25 SELF, レベル 35, レッスン 0
ロック未解除
セーブファイルのサイズはどれくらいですか? 💾
セーブファイルのサイズはどれくらいですか? 💾
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION