従来の入出力ストリーム処理では、一般的に使用される構造は次のとおりです。try-catch-finally 構造は、関連する例外をキャプチャするために使用されます。最後に、例外があるかどうかに関係なく、ストリームを閉じます。
try {
//todo
} catch (IOException e) {
log.error("read xxx fail,{}", e.getMessage())
} finally {
try {
if (in != null)in.close();
if (out != null)out.close();
} catch(Exception e) {
log.error("stream close error,{}", e.getMessage())
}
}
jdk1.7 以降では、ストリームを自動的に閉じることができる try() {} catch(IOException e){} を使用して io ストリームを処理することをお勧めします。以下に示すのは、ファイルの内容を 1 行ずつ読み取る簡単な例です。
package com.xxx.io;
import java.io.FileNotFoundException;
import java.nio.file.Paths;
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
String filePath = "conf/test.txt";
try (Scanner scanner = new Scanner(Paths.get(filePath).toFile())) {
String line;
while (scanner.hasNextLine()) {
line = scanner.nextLine();
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.out.println("read file error : " + e.getMessage());
}
}
}
追跡とデバッグが可能です。try(){} の実行が終了すると、scanner.close() メソッド本体に入ります。
try(){} コード ブロックを使用した後、コードの実行が完了した後、ストリームを自動的に閉じることができます。これは従来の方法よりもはるかに簡単です。
try() 部分には、セミコロンで区切られた複数のステートメントを含めることも、入力ストリームと出力ストリームの両方を含めることもできます。最後に、実行は完了し、一律に閉じられます。
いくつかの記事では、これはさまざまなストリームが AutoCloseable インターフェイスを実装し、close() メソッドを実装しているためであると述べられていましたが、実際、先ほど実装した Scanner クラスは AutoCloseable を実装せず、Closeable インターフェイスのみを実装しました。
次の例では、クラスをカスタマイズし、Closeable インターフェイスを実装し、close() メソッドをオーバーライドします。
package com.xxx.io;
import java.io.Closeable;
public class AutoCloseTest implements Closeable {
@Override
public void close() {
System.out.println("closed");
}
public void work() {
System.out.println("work");
}
public static void main(String[] args) {
try (AutoCloseTest test = new AutoCloseTest()){
test.work();
}
}
}
プログラムを実行すると、最終的に出力される結果は次のようになります。
同様に、try(){} を実行した後、ストリームの close() メソッドが実行されます。
その理由は、コンパイラがこのコードをコンパイルした後、生成されるコードは実際には従来の try{}catch()finally{} 構造に似ているためです。
つまり、コード try(){} は実際には try(){}catch()finally{} の短縮形です。ストリームが自動的に閉じるのは、それほど魔法のようなものではないようです。
もう 1 つの質問があります。ここのコード ブロック内の try(){} だけが例外をキャッチしません。最終コードがコンパイルされた後、キャッチ例外が追加されます。コードにキャッチ例外がある場合、それは原因ではありませんか?例外情報は失われますか? 実際には、いいえ、今コンパイルしたスキャナーのサンプルの結果を見てみましょう。
ここで try(){} がコンパイルされた後、例外がキャッチされた場合は addSuppressed() オペレーションが実行され、外側の try{}catch{} は引き続き例外をキャッチできます。