Java 6 以前でのテキストファイル読み込み

Java 6 以前で面倒だったのは主に以下の2点。

  • try ブロックの外で変数を宣言しておかないと、finally でリソース開放処理ができない
  • リソース解放するための close メソッドで例外が出たときのことを考慮しないといけない

サンプルコード。


import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Cat{

  public static void main(String[] args){

    String filePath = args[0];

    // try ブロックの外で変数を宣言しておかないと、
    // finally でリソース開放処理ができない
    FileReader fr     = null;
    BufferedReader br = null;

    try{
      fr = new FileReader(filePath);
      br = new BufferedReader(fr);
      String line = br.readLine();
      while(line != null){
        System.out.println(line);
        line = br.readLine();
      }
      br.close();

    }catch(IOException e){
      e.printStackTrace();

    }finally{
      try{
        if(br != null){
          // BufferedReader#close は
          // コンストラクタで渡された
          // FileReader#close を呼んでくれるので
          // FileReader#close を自前で呼び出す必要は無い
          br.close();
        }
      }catch(IOException e2){
        // BufferedReader#close で例外が出たら
        // どうしようもない
        e2.printStackTrace();
      }
    }
  }
}

Java 6 以前では、もう少しラクに書けるようにと、リソース解放処理をラッピングするクラスなど作っていた人も多いんじゃないかなと思う。

Java 7 でのテキストファイル読み込み

try-with-resources を利用すると、リソースの解放を自動でやってくれるのでラクになった。

すべての例外は catch ブロックに集まる (2つ以上の例外が発生した場合は Throwable クラスの getSuppressed メソッドですべて取得できる)。

サンプルコード。


import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Cat7{

  public static void main(String[] args){

    String filePath = args[0];

    try(BufferedReader br = new BufferedReader(new FileReader(filePath))){
      String line = br.readLine();
      while(line != null){
        System.out.println(line);
        line = br.readLine();
      }
      br.close();

    }catch(IOException e){
      // catch ブロックは
      // BufferedReader#close が呼ばれてから
      // 実行される
      e.printStackTrace();

    }finally{
      // finally ブロックは
      // BufferedReader#close が呼ばれてから
      // 実行される
    }
  }
}
try-with-resources 文は、1 つ以上のリソースを宣言する try 文です。リソースは、プログラムでの使用が終わったら閉じられなければいけないオブジェクトです。try-with-resources 文は、文の終わりで各リソースが確実に閉じられるようにします。java.io.Closeable を実装しているすべてのオブジェクトも含め、java.lang.AutoCloseable インタフェースを実装しているオブジェクトはリソースとして使用できます。

try-with-resources 文

BufferedReader の close メソッドの実装はどうなっているのか

リファレンスを見てみた。BufferedReader クラスの close メソッドの説明には「ストリームを閉じて、それに関連するすべてのシステムリソースを解放します」とある。

ストリームを閉じて、それに関連するすべてのシステムリソースを解放します。ストリームが閉じられたあとに read()、ready()、mark()、reset()、または skip() を呼び出すと、IOException がスローされます。すでに閉じられているストリームを閉じても、何の影響もありません。

BufferedReader (Java Platform SE 7 )

ドキュメントだけではなんだか不安なので、OpenJDK のソースコードも見てみた。

BufferedReader の close メソッドをコールすると、 コンストラクタで渡した Reader インスタンスの close メソッドをコールしてくれるのがわかる。


private Reader in;

private char cb[];

(中略)
public BufferedReader(Reader in, int sz) {
    super(in);
    if (sz <= 0)
        throw new IllegalArgumentException("Buffer size <= 0");
    this.in = in;
    cb = new char[sz];
    nextChar = nChars = 0;
}

(中略)

public void close() throws IOException {
    synchronized (lock) {
        if (in == null)
            return;
        try {
            in.close();
        } finally {
            in = null;
            cb = null;
        }
    }
}

BufferedReader のソースコード
jdk8/jdk8/jdk: 687fd7c7986d src/share/classes/java/io/BufferedReader.java

ref. [ヅ] Java の AutoCloseable インターフェースを実装して try-with-resources の動作を理解する (2015-04-22)

tags: java

Posted by NI-Lab. (@nilab)