何が言いたいの?
開いたら必ず閉じようということです。
ファイルが開きっぱなしになっていると、場合によってはファイルの読み書きが行えなくなります。
それでは困りますね。
例えば?
FileReader
クラスやFileWriter
クラスを用いて、テキストファイルを読み書きする場合などです。
昔はBufferdReader
クラスなんかを使っていましたが、今どきはScanner
クラスなるものが使われているようですね。
実際にテキストファイルの内容を読み込み、内容をコンソールに出力するプログラムを以下に示します。
import java.io.File;
import java.util.Scanner;
public class FileOpenTest {
public static void main(String[] args) {
try {
// テキストファイルを開く
Scanner scanner = new Scanner(new File("F1歴代チャンピオン一覧.csv"));
// 1行ずつ読み込み、コンソールに出力
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
// テキストファイルを閉じる
scanner.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
1950年,ジュゼッペ・ファリーナ,アルファロメオ,-
1951年,ファン・マヌエル・ファンジオ,アルファロメオ,-
1952年,アルベルト・アスカリ,フェラーリ,-
1953年,アルベルト・アスカリ,フェラーリ,-
1954年,ファン・マヌエル・ファンジオ,マセラティ/メルセデス,-
1955年,ファン・マヌエル・ファンジオ,メルセデス,-
1956年,ファン・マヌエル・ファンジオ,フェラーリ,-
1957年,ファン・マヌエル・ファンジオ,マセラティ,-
1958年,マイク・ホーソーン,フェラーリ,ヴァンウォール
1959年,ジャック・ブラバム,クーパー・クライマックス,クーパー
(内容はwikipediaより借用)
無事、テキストファイルの読み込みに成功しました🎉
では終わりません
ほとんどの場合、読み込んだ行に対して何らかの加工を行います。
// 1行ずつ読み込み、何らかの処理を行う
while (scanner.hasNextLine()) {
anyProcess(scanner.nextLine());
}
もし、この処理内で例外が発生したらどうなるでしょう。
今回の場合try
文で処理全体を囲っているので、catch
ブロックへ処理が移ります。
} catch (Exception e) {
e.printStackTrace();
}
これで無事に例外を補足し、エラーログを出力することができました🎉
ではありません
一つ忘れていることがあります。
例外が発生したらcatch
ブロックへ処理が移る...
そうです。
ファイルが閉じられていません😲
try {
// テキストファイルを開く
Scanner scanner = new Scanner(new File("歴代チャンピオン一覧.csv"));
// 1行ずつ読み込み、何らかの処理を行う
while (scanner.hasNextLine()) {
anyProcess(scanner.nextLine()); // エラーが発生
}
// テキストファイルを閉じる
scanner.close(); // 実行されない!!
} catch (Exception e) {
e.printStackTrace(); // ここまで処理が飛んでしまう
}
どうすればいいの
何がなんでもファイルを閉じたい...
そこでfinally
ブロックの登場です。
finally
ブロックは、処理にエラーがあったか無かったかに関わらず必ず実行されます。
finally
ブロックを追加したコードを以下に示します。
import java.io.File;
import java.util.Scanner;
public class FileOpenTest {
public static void main(String[] args) {
// try文のブロックの外に変数を宣言する
Scanner scanner = null;
try {
// テキストファイルを開く
scanner = new Scanner(new File("歴代チャンピオン一覧.csv"));
// 1行ずつ読み込み、何らかの処理を行う
while (scanner.hasNextLine()) {
anyProcess(scanner.nextLine());
}
// テキストファイルを閉じる
scanner.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
// テキストファイルを閉じる
if (scanner != null) {
scanner.close();
}
}
}
}
まず、try
文のブロックの外側にScanner
クラスの変数を宣言します。
try
catch
finally
は、それぞれ異なるスコープと見なされるためです。
try
ブロックの中で宣言された変数は、finally
ブロックから参照することができないのです。
その後、実際にファイルを閉じていきます。
この時、scannar
変数がnull
かどうかの判定を忘れてはいけません。
scannar
変数は宣言時にnull
を格納しています。
// try文のブロックの外に変数を宣言する
Scanner scanner = null;
null
に対してメソッドの呼び出しを実行すると、これまた別の例外が発生してしまいます。
書いてる途中で知ってしまった
try-with-resources
文なるものがあるようです
どう考えてもこっちの方が便利ですよね。C#
のUsing
みたい。
参考:Oracleのサイト
まとめ
少々ばかり内容が古臭い気がしますが...まあ良いでしょう。
開いたら閉じる。便器のフタと同じです。