IOストリーム

公開日: 2026-07-29 09:12 更新日: 2026-07-29 09:12 3316文字 17 min read ... ページビュー

この記事では、Javaにおけるファイル操作とIOストリームのコア内容を体系的に紹介し、ファイルクラスのパス処理、ファイルとディレクトリの作成、再帰操作、バイトストリームと文字ストリームの使用方法をカバーしています。IOフローの分類、ノードフローと処理フローの組み合わせの使用、ファイルのコピー、テキストの読み書き、オブジェクトのシリアル化などの一般的な操作、プロファイル処理とApache POIによるExcelファイルの読み書きの実践に焦点を当てています。リソース管理、コーディング仕様、セキュリティ、および実用的なアプリケーションのベストプラクティスを強調します。

IOストリーム

Fileクラス

Fileは、ファイルまたはディレクトリパスの抽象表現です。Fileオブジェクトの作成は、メモリ内にパスオブジェクトを作成するだけであり、ディスク上にファイルやディレクトリが存在する必要はありません。

コンストラクター

File file1 = new File("d:/lesson/java2601/a.txt");
File file2 = new File("d:/lesson/java2601", "a.txt");
File parent = new File("d:/lesson/java2601");
File file3 = new File(parent, "a.txt");

Windowsのパス文字列では、エスケープ後のバックスラッシュを使用できます。またはスラッシュを使用できます。

ファイルおよびディレクトリの作成

ファイルの作成

File file = new File("d:/lesson/java2601/test/a.txt");

try {
    boolean created = file.createNewFile();
    System.out.println(created ? "创建成功" : "文件已存在");
} catch (IOException e) {
    e.printStackTrace();
}

createNewFile()は、存在しない親ディレクトリを自動的に作成しません。

ファイル拡張子は通常、オペレーティングシステムやアプリケーションがファイルタイプを識別するのに役立ちますが、拡張子自体はファイルの内容を変更しません。

ディレクトリの作成

mkdir()は、親ディレクトリがすでに存在する場合にのみ、単一レベルのディレクトリを作成できます。

File directory = new File("d:/lesson/java2601/test/a");
directory.mkdir();

mkdirs()では、欠落しているマルチレベルの親ディレクトリを同時に作成できます。

File directory = new File("d:/lesson/java2601/test/a/b/c");
directory.mkdirs();

テンポラリファイルの作成

File directory = new File("d:/lesson/java2601/test");
try {
    File tempFile = File.createTempFile("temp", ".txt", directory);
    System.out.println(tempFile);
} catch (IOException e) {
    e.printStackTrace();
}

一時ファイル名の接頭辞には、少なくとも3 文字が必要です。

一般的なアプローチ

方法作用
existsパスが存在するかどうか
isFile通常文書かどうかの判断
isDirectoryカタログかどうかの判断
deleteファイルまたは空のディレクトリの削除
getNameファイル名またはディレクトリ名の取得
getParentFile親パスに対応するFileオブジェクトを取得する
getPathオブジェクトを構築する際に使用するパス形式を取得する
getAbsolutePath绝対パス文字列の取得
通常のファイル長をバイト単位で取得
listFilesディレクトリの直接の子を取得する

listFiles()は、パスがディレクトリでない場合、IOエラーが発生した場合、またはアクセス権がない場合にnullを返すことがあります。使用する前に確認してください。

File directory = new File("d:/lesson/java2601");
File[] files = directory.listFiles();

if (files != null) {
    for (File file : files) {
        System.out.println(file);
    }
}

File.equals()は抽象パス名が等しいかどうかを比較し、相対パス、シンボリックリンク、または..を含むパスのすべてを自動的に正規化しない。実際のスペシフィケーションパスを比較する必要がある場合は、getCanonicalFile()を使用し、IOExceptionを処理できます

再帰的に

再帰はメソッド自身を直接または間接的に呼び出すことです。再帰には明確な終了条件が必要で、呼び出しごとに終了条件に近づくようにします。

階乗は

public static long factorial(int number) {
    if (number < 0) {
        throw new IllegalArgumentException("阶乘参数不能为负数");
    }
    if (number <= 1) {
        return 1;
    }
    return number * factorial(number - 1);
}

フィボナッチ数列

public static long fibonacci(int number) {
    if (number < 0) {
        throw new IllegalArgumentException("参数不能为负数");
    }
    if (number <= 1) {
        return number;
    }
    return fibonacci(number - 1) + fibonacci(number - 2);
}

このような直接再帰は多くのサブ問題を繰り返し計算し、再帰のデモンストレーションにのみ適している。大きなデータを処理する場合は、ループまたはキャッシュ結果を使用してください。

ファイルの再帰検索

public static void findPdfFiles(File path, List<File> result) {
    if (path == null || !path.exists()) {
        return;
    }

    if (path.isFile()) {
        if (path.getName().toLowerCase().endsWith(".pdf")) {
            result.add(path);
        }
        return;
    }

    File[] children = path.listFiles();
    if (children == null) {
        return;
    }

    for (File child : children) {
        findPdfFiles(child, result);
    }
}

ディレクトリの再帰的な削除

public static boolean deleteRecursively(File path) {
    if (path == null || !path.exists()) {
        return true;
    }

    if (path.isDirectory()) {
        File[] children = path.listFiles();
        if (children == null) {
            return false;
        }

        for (File child : children) {
            if (!deleteRecursively(child)) {
                return false;
            }
        }
    }

    return path.delete();
}

ディレクトリを再帰的に扱うときは、アクセス権、シンボリックリンク、深すぎるディレクトリによるスタックオーバーフローにも注意してください。最新のJavaプロジェクトでは、java.nio.file.Filesが提供するファイルツリー APIも使用できます。

IOフローの概要

IOはInputとOutputを意味する。プログラムメモリを参照する:

  • 入力ストリームは外部データをプログラムに読み込む。
  • 出力ストリームはプログラムデータを外部デバイスに書き込みます。

データ単位別の分類

  • InputStreamおよびOutputStreamアーキテクチャはバイト単位で処理され、画像、オーディオ、ビデオ、その他のバイナリデータに適しています。
  • ReaderWriterシステムは文字単位で処理され、テキストデータに適している。

文字ストリームは、バイトを文字エンコーディングで文字にデコードするか、文字をバイトにエンコードする必要があります。

接続先別の分類

  • ノードストリームは、ファイル、メモリ、ネットワークなどのデータソースまたはターゲットに直接接続します。たとえば、FileInputStreamです。
  • 処理ストリームは他のストリームをラップし、バッファリング、データ型の読み書き、オブジェクトのシリアライズなどの機能を追加します(BufferedInputStreamObjectOutputStreamなど)。

処理フローは、必ずしもノードフローよりも“高度”または“高速”である必要はなく、必要な機能に応じて組み合わせて使用する必要があります。

ステップの使用

  1. インポートかエクスポートかを決定します。

  2. バイトと文字を処理するかを決定します。

  3. 実際のデータソースに対応するノードフローを選択します。

  4. 必要に応じて、バッファ、コード変換、またはオブジェクト処理ストリームを増やします。

  5. try-with-resourcesを使用してリソースをタイムリーにシャットダウンします。

FileInputStreamとFileOutputStreamの比較

バイトの読み取り

FileInputStreamの一般的なread()法は以下の通りである。

  • read():1バイトを読み込み、0から255を返し、最後に-1を返す。
  • read(byte[] buffer):配列長を最大バイト読み込み、実際の読み込み数を返します。
  • read(byte[] buffer, int offset, int length):配列の指定された範囲にデータを読み込む。

一括読み取りの場合は、今回実際に読み取った長さだけを処理できます。

try (FileInputStream input = new FileInputStream(
        "d:/lesson/java2601/test/a.txt")) {

    byte[] buffer = new byte[1024];
    int length;

    while ((length = input.read(buffer)) != -1) {
        for (int i = 0; i < length; i++) {
            System.out.println(buffer[i]);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

テキストコンテンツを出力するときは、バイトを1つずつ文字に変換せず、文字ストリームを使用して正しいエンコーディングを指定してください。

バイトの書き込み

FileOutputStreamはデフォルトで元のファイルを上書きし、コンストラクタの第 2 引数がtrueの場合に追加を示します。

try (FileOutputStream output = new FileOutputStream(
        "d:/lesson/java2601/test/a.txt", true)) {

    byte[] data = "ABCDEFG".getBytes(StandardCharsets.UTF_8);
    output.write(data);
} catch (IOException e) {
    e.printStackTrace();
}

ファイルをコピーする

public static void copyFile(String source, String target) {
    try (FileInputStream input = new FileInputStream(source);
         FileOutputStream output = new FileOutputStream(target)) {

        byte[] buffer = new byte[8192];
        int length;

        while ((length = input.read(buffer)) != -1) {
            output.write(buffer, 0, length);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

コピーバイナリにはバイトストリームを使用する必要があります。

BufferedInputStreamとBufferedOutputStream

バッファされたバイトストリームはメモリ内のバッファを保持し、下位の読み書き呼び出しの数を減らします。

public static void copyFileWithBuffer(String source, String target) {
    try (BufferedInputStream input = new BufferedInputStream(
            new FileInputStream(source));
         BufferedOutputStream output = new BufferedOutputStream(
            new FileOutputStream(target))) {

        byte[] buffer = new byte[8192];
        int length;

        while ((length = input.read(buffer)) != -1) {
            output.write(buffer, 0, length);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

バッファは、出力ストリームを閉じると最初にフラッシュされます。ストリームを開いたまますぐにデータをターゲットに送信する必要がある場合、flush()を呼び出すことができます。

FileReaderとFileWriter

FileReaderFileWriterはテキストの読み書きに使用されますが、従来のコンストラクタは通常、プラットフォームのデフォルト文字セットを使用します。ファイルエンコーディングが固定されている場合は、InputStreamReaderOutputStreamWriterを使用して文字セットを明示的に指定することをお勧めします。

try (FileReader reader = new FileReader(
        "d:/lesson/java2601/test/a.txt")) {

    char[] buffer = new char[1024];
    int length;

    while ((length = reader.read(buffer)) != -1) {
        System.out.print(new String(buffer, 0, length));
    }
} catch (IOException e) {
    e.printStackTrace();
}

FileReader自体はBufferedReaderではなく、ラインバッファ機能を自然に有するとは言えない。

try (FileWriter writer = new FileWriter(
        "d:/lesson/java2601/test/a.txt")) {
    writer.write("1234567890");
} catch (IOException e) {
    e.printStackTrace();
}

BufferedReaderとBufferedWriter

BufferedReaderは、テキストを行単位で読み取ることができるreadLine()を提供します。返される文字列には行末文字が含まれておらず、ファイルの末尾に到達するとnullが返されます。

try (BufferedReader reader = new BufferedReader(
        new FileReader("d:/lesson/java2601/test/a.txt"))) {

    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

BufferedWriterは、newLine()書き込みプラットフォームに対応する行セパレータを提供する。

try (BufferedWriter writer = new BufferedWriter(
        new FileWriter("d:/lesson/java2601/test/a.txt"))) {

    writer.write("第一行");
    writer.newLine();
    writer.write("第二行");
} catch (IOException e) {
    e.printStackTrace();
}

InputStream ReaderとOutputStream Writer

これら2つの変換ストリームはバイトストリームと文字ストリームを接続し、文字コード変換を担当します。

try (BufferedReader reader = new BufferedReader(
        new InputStreamReader(
                new FileInputStream("d:/lesson/java2601/test/a.txt"),
                StandardCharsets.UTF_8))) {

    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}
try (BufferedWriter writer = new BufferedWriter(
        new OutputStreamWriter(
                new FileOutputStream("d:/lesson/java2601/test/a.txt"),
                StandardCharsets.UTF_8))) {

    writer.write("使用 UTF-8 写入文本");
} catch (IOException e) {
    e.printStackTrace();
}

DataInputStreamおよびDataOutputStream

データストリームは、Java 基本型のバイナリ形式でデータを読み書きできます。

try (DataOutputStream output = new DataOutputStream(
        new FileOutputStream("d:/lesson/java2601/test/data.bin"))) {

    output.writeInt(1024);
    output.writeDouble(100.0);
    output.writeUTF("Java");
} catch (IOException e) {
    e.printStackTrace();
}

読み込みには同じ順序と型を使用する必要があります。そうしないと、エラー結果が読み込まれたり、例外がスローされたりします。

try (DataInputStream input = new DataInputStream(
        new FileInputStream("d:/lesson/java2601/test/data.bin"))) {

    int number = input.readInt();
    double price = input.readDouble();
    String text = input.readUTF();

    System.out.println(number);
    System.out.println(price);
    System.out.println(text);
} catch (IOException e) {
    e.printStackTrace();
}

オブジェクトの直列化とデ直列化

シリアライズはオブジェクトとそのシリアライズ可能なオブジェクトグラフをバイト列に変換し、デシリアライズはバイト列からオブジェクトを復元する。

クラスにはSerializableタグインタフェースの実装が必要。

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private transient String password;
}

transientフィールドはデフォルトのシリアライズには関与しません。serialVersionUIDは、シリアル化データが現在のクラスバージョンと互換性があるかどうかを判断するために使用されます。

バイト配列へのシリアル化

public static byte[] serialize(Object object) throws IOException {
    try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
         ObjectOutputStream output = new ObjectOutputStream(bytes)) {

        output.writeObject(object);
        return bytes.toByteArray();
    }
}

バイト配列からのデシリアライズ

public static Object deserialize(byte[] data)
        throws IOException, ClassNotFoundException {

    try (ByteArrayInputStream bytes = new ByteArrayInputStream(data);
         ObjectInputStream input = new ObjectInputStream(bytes)) {

        return input.readObject();
    }
}

シリアル化されたデータにJDKイテレーションが完全に一貫している必要はありませんが、クラス名、フィールド構造、serialVersionUID、および関連クラスが互換性条件を満たしている必要があります。

信頼できないデータのデシリアライズは危険なオブジェクトの振る舞いを引き起こす可能性があり、実際のプロジェクトでは信頼できないソースからのバイトストリームを直接デシリアライズしないでください。

PrintWriter

PrintWriterは、print()println()printf()などの便利なテキスト出力方法を提供します。

try (PrintWriter writer = new PrintWriter(
        "d:/lesson/java2601/test/a.txt",
        StandardCharsets.UTF_8.name())) {

    writer.println("你好");
    writer.println("中午吃什么");
} catch (FileNotFoundException | UnsupportedEncodingException e) {
    e.printStackTrace();
}

PrintWriterの部分書き込みエラーはIOExceptionで直接スローされず、必要に応じてcheckError()を呼び出してステータスをチェックできます。ストリームを閉じると、バッファが自動的にフラッシュされます。

Propertiesクラス

Propertiesは、文字列キー値の構成を保持するために使用され、Hashtable<Object, Object>から継承されます。構成の読み取りにはgetProperty()を、書き込みにはsetProperty()を優先してください。

プロファイルは、デプロイメント環境によって調整できるが、プログラムにハードコードしないデータを保持するのに適しています。

一般的な構成フォーマット

  • XMLには明確な構造があるが、記述と解析は比較的複雑である。
  • プロパティは単純なキー形式を使用し、階層表現は限られます。
  • YAMLは強力な階層表現を持ち、Spring Bootなどのフレームワークでよく使われます。

設定の読み取り

p1.name=Tom
p1.age=20
p2.name=Jerry
p2.age=22
Properties properties = new Properties();

try (Reader reader = new InputStreamReader(
        new FileInputStream("src/myinfo.properties"),
        StandardCharsets.UTF_8)) {

    properties.load(reader);

    Person person1 = new Person();
    person1.name = properties.getProperty("p1.name");
    person1.age = Integer.parseInt(properties.getProperty("p1.age"));

    Person person2 = new Person();
    person2.name = properties.getProperty("p2.name");
    person2.age = Integer.parseInt(properties.getProperty("p2.age"));
} catch (IOException e) {
    e.printStackTrace();
}

Readerを使用すると、文字セットを明示的に選択できますJava 8でInputStreamを使用してプロパティを直接読み取る場合、従来のフォーマットはISO-8859-1ルールで扱われ、非 ASCII 文字は通常 Unicodeエスケープを必要とする。

書き込み設定

properties.setProperty("p3.name", "Tom");
properties.setProperty("p3.age", "22");

try (Writer writer = new OutputStreamWriter(
        new FileOutputStream("src/myinfo.properties"),
        StandardCharsets.UTF_8)) {

    properties.store(writer, "第三个人的信息");
} catch (IOException e) {
    e.printStackTrace();
}
image-001
image-001

Apache POI

Apache POIはApache Software FoundationのJavaプロジェクトであり、ExcelやWordなどのMicrosoft Office 形式の読み書きが可能である。

典型的な演習は、学生の成績ブックからデータを読み取り、平均を計算し、結果を別のワークシートに書き込むことです。Excelでは、ファイル形式に基づいてモデルを選択する必要があります。

  • .xlsは通常 HSSFを使用している。
  • .xlsxは通常 XSSFを使用している。

POIを使用してワークブック、入力ストリーム、および出力ストリームを読み書きする場合も、try-with-resourcesを使用してリソースを適切にクローズし、セル·タイプ、空のセル、および数式セルの処理に注意してください。

気に入ったならばコメントを残してくださいね~

... ページビュー
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka