LoginSignup
0
1

More than 3 years have passed since last update.

【弊社新人に向けて】開いたら必ず閉じよう

Posted at

何が言いたいの?

開いたら必ず閉じようということです。
ファイルが開きっぱなしになっていると、場合によってはファイルの読み書きが行えなくなります。
それでは困りますね。

例えば?

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より借用)

無事、テキストファイルの読み込みに成功しました🎉:clap:

では終わりません

ほとんどの場合、読み込んだ行に対して何らかの加工を行います。

何らかの処理
            // 1行ずつ読み込み、何らかの処理を行う
            while (scanner.hasNextLine()) {
                anyProcess(scanner.nextLine());
            }

もし、この処理内で例外が発生したらどうなるでしょう。
今回の場合try文で処理全体を囲っているので、catchブロックへ処理が移ります。

例外を補足
        } catch (Exception e) {
            e.printStackTrace();
        }

これで無事に例外を補足し、エラーログを出力することができました🎉:clap:

ではありません

一つ忘れていることがあります。
例外が発生したら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ブロックを追加したコードを以下に示します。

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を格納しています。

初期値には null を設定
        // try文のブロックの外に変数を宣言する
        Scanner scanner = null;

nullに対してメソッドの呼び出しを実行すると、これまた別の例外が発生してしまいます。

書いてる途中で知ってしまった

try-with-resources文なるものがあるようです
どう考えてもこっちの方が便利ですよね。C#Usingみたい。
参考:Oracleのサイト

まとめ

少々ばかり内容が古臭い気がしますが...まあ良いでしょう。
開いたら閉じる。便器のフタと同じです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1