Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

Streamをcloseする処理について

Streamをcloseする処理で奇妙な現象に悩まされています。
もしかしたら既に常識なのかもしれませんが、ネットで調べてもうまい例が見つかりませんでした。

具体的には、close処理は必ずfinally節でやるようにと一般的に言われています。
しかし、whileでinputStreamを生成、closeした場合、2度目の生成でもなぜかinputStreamのオブジェクトにアクセスできず、IOExceptionが発生してしまいます。
そのため、現在は成功時にのみcloseするようにしています。
これは同じ参照で生成したオブジェクトは一度closeされているともうアクセスできないというJavaの仕様なのでしょうか。それとも、私のコーディングがまずいのでしょうか。
このような問題を回避する一般的なコーディングパターンは存在するのでしょうか。
それとも成功時にのみcloseする方が正しいのでしょうか。

以下がコードの例です。
それぞれのメソッドを実行するとコンソールから入力受付状態になり、
数値以外が入力されると例外を投げます。
例外を投げた後、再度入力させるのが狙いです。

/*
 * ストリームを使いまわす例(成功時のみcloseする)
 * 
 */
public static void testClose_try(){

while(true){

    //reader というバッファーを作り、標準入力を代入
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1);

    try{

        System.out.println("数値を入力してください。");

        // reader の一行を読んでinput1 に代入
        String input1 = reader.readLine();

        // input1 を int型に変換して、num1 に代入する
        int num1 = Integer.parseInt(input1);

        // 成功したらストリームを閉じます
        reader.close();

        break;

    } catch (NumberFormatException e){
    System.out.println("数値以外は入力できません。再入力してください。");
    continue;

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

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

    } 

    }

}


/*
 * 成功、失敗に関係なくストリームを閉じる例
 * 
 */
public static void testClose_finally(){

    while(true){

    //reader というバッファーを作り、標準入力を代入
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1);

    try{

        System.out.println("数値を入力してください。");

        // reader の一行を読んでinput1 に代入
        String input1 = reader.readLine();

        // input1 を int型に変換して、num1 に代入する
        int num1 = Integer.parseInt(input1);

        break;

    } catch (NumberFormatException e){
    System.out.println("数値以外は入力できません。再入力してください。");
    continue;

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

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

    } finally {

    // ストリームを閉じます
    try {
    reader.close();
    } catch (IOException e) {
    e.printStackTrace();
    }

    }

    }

}





11

回答

118673

閲覧

11件の回答

評価

0

普通はね、ストリーム変数を宣言して、
try節に入り、
ストリームのインスタンスを生成してから、
whileループに入るんだよ。

評価

0

補足です。
環境はJava5.0、6.0両方で同じ現象でした。

評価

0

不良社員 さん
ご回答ありがとうございます。

>回答内容 普通はね、ストリーム変数を宣言して、
try節に入り、
ストリームのインスタンスを生成してから、
whileループに入るんだよ。 

この普通というのは何か根拠やパターンが存在するのでしょうか?
成功時に閉じることが正解なのでしょうか。
ということは失敗時には閉じてはいけないということですよね。
また同じ参照でStreamオブジェクトを生成してもclose後はアクセスできないというのもJavaの仕様なのでしょうか?

評価

0

エラーになる原因は、BufferedReaderをクローズするときに、System.inも閉じられてしまうからだね。
これはシステムの標準入力ストリームだから、
閉じる必要はないです。

つか、finallyが重要だって言われるのは、
閉じる処理が必要な場合は、必ずfinallyを使えってことでしょ。

>この普通というのは何か根拠やパターンが存在するのでしょうか?

パターンは、場数を踏めば身につきます。

んが。

根拠って・・・。
根拠も無しに書いてると思ってるの?

IOExceptionとNumberFormatExceptionを、
ひとつのtry-catchで処理しようなんて、
んなおかしなコード書くようなひとに言われたくはないな。

毒皿だし、ついでに書いておくか。

このおかしなコードを普通のコードにするには、
IOExceptionとNumberFormatExceptionへの対処を分割する必要がある。

上に書いた普通のパターンに補足すると、
whileループの外側のtry-catchで、IOExceptionに対処する。
finallyが必要ならこちらに。
そして、もう一つ。whileループの内側に
try-catchを設けて、NumberFormatExceptionに対処するのだ。

こんなところか。

評価

0

>close後はアクセスできないというのも

close()は、リソースの解放なので、
リソースの解放後はアクセスできないのは、
ごく当然だと思うのですが、
私の誤解でしょうか。

評価

0

もしかして、BufferedReaderというオブジェクトだけをcloseしてると思ってない?

評価

0

例えばファイルなら、ストリームを生成する必要があるけど、
System.inは、最初からストリームだからね。
知らないと、その辺で詰まるのかも。

評価

30

>$様
docsを見ても"ストリームを閉じます。"としか書いていないので、誤解があるかもしれませんね。
>不良生徒様
同意です。

ちょっとサンプルで作ってみました。
# import文は省略しました。

public class MyInputStreamUser {
public static void main(String[] args) {
        BufferedReader br = null;
        String s ;
        int num;
        try {
            br =new BufferedReader(new InputStreamReader(new MyFilterInputStream(System.in)));    
        while(true){
                s = br.readLine();
                try{
                num = Integer.parseInt(s);
                }catch (NumberFormatException e) {
                    System.out.println(s +"は数字に出来ないよ?");
                    continue;
                }
                System.out.println(num);
                break;            
            }
        } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }    
        }
}
}
class MyFilterInputStream extends FilterInputStream{
    protected MyFilterInputStream(InputStream in) {
        super(in);
    }
    @Override
    public void close() throws IOException {
    }
}

FilterInputStreamを継承して、
BufferedReaderのcloseを騙してみよう
(System.inは影響与えないように)
という試みです。。。

# 何か改善点等御座いましたら是非仰って頂けると幸いです。

評価

0

補足です。
リソースの解放について。
http://www.asahi-net.or.jp/~DP8T-ASM/java/tips/ReleaseResource.html

蛇足でしたらゴメンナサイ。

評価

0

そもそも、なぜcloseする必要が?

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。