最近書いたコード
ストリーム関係の処理をするときはストリームに対する処理を try ブロックで囲って finally で必ずクローズするようにするのが割と定石ですよね?
例えば↓のように。このとき、私はできる限り外側(下記の例では BufferedInputStream)のストリームを閉じるようにしています。
InputStream in = null; try { in = new BufferedInputStream( new FileInputStream( ... ) ); in.read();// ストリームに対する処理 } finally { if( in!=null ) { try { in.close(); } catch( IOException ex ) { // N/A logger.log( Level.WARNING, "ストリームのクローズでIO例外が発生しました。", ex ); } } }
ところがこれと同じことを ObjectInputStream に対して行なう場合は、ちょっとだけ注意が必要だったりします。
ObjectInputStream は生成直後に元のストリームから数バイト読み込むため、コンストラクタが IOException を送出する可能性があります。このため、以下のようなコードではストリームのクローズ処理が正しく行なわれない可能性あります。
ObjectInputStream in = null; try { in = new ObjectInputStream( new BufferedInputStream( new FileInputStream( ... ) ) ); Object obj = in.readObject(); } finally { if( in!=null ) { try { in.close(); } catch( IOException ex ) { // N/A logger.log( Level.WARNING, "ストリームのクローズでIO例外が発生しました。", ex ); } } }
もし、ObjectInputStream のコンストラクタで例外が発生した場合、 in 変数は null のままのため、new BufferedInputStream( new FileInputStream( ... ) ) で開かれたストリームは閉じられません。(ObjectOutputStreamを使用したときも同様)
なので、ObjectInputStream のように、コンストラクタが例外を返す可能性のあるストリームを使用する場合、私は以下のように書いています。
InputStream in = null; try { in = new BufferedInputStream( new FileInputStream( ... ) ); ObjectInputStream objIn = new ObjectInputStream( in ); in = objIn; Object object = objIn.readObject(); } finally { if( in!=null ) { try { in.close(); } catch( IOException ex ) { // N/A logger.log( Level.WARNING, "ストリームのクローズでIO例外が発生しました。", ex ); } } }
変数 in に、段階を経てストリームのオブジェクトを代入することで、たとえ ObjectInputStream のコンストラクタで例外が発生しても、その直前で開かれたストリームは閉じるようにしています。
ちなみに in に objIn を代入しなおしているのは、できる限り外側のストリームを閉じるという自分のポリシーに従ったためです。