ページ 11

PrintWriterのclose処理

Posted: 2015年2月14日(土) 13:29
by march3
お世話になります。
毎回初歩的な質問で申し訳ないのですが、
Javaのファイル出力処理でPrintWriterを使うことは珍しくないと思います。

例えば、before.txtをafter.txtにコピーする単純なプログラムだと以下のような感じになると思います。

コード:

public class Copy {
	public static void main(String[] args) {
		BufferedReader reader = null;
		PrintWriter writer = null;
		try {
			reader = new BufferedReader(new FileReader("before.txt"));
			writer = new PrintWriter(new BufferedWriter(new FileWriter("after.txt")));
			String line = null;
			while ((line = reader.readLine()) != null) {
				writer.println(line);
			}
		} catch (FileNotFoundException e) {
			/* 適宜例外処理 */
		} catch (IOException e) {
			/* 適宜例外処理 */
		} finally {
			try {
				if (reader != null) {
					reader.close();
				}
			} catch (IOException e) {
				/* 適宜例外処理 */
			}
			finally{
				if(writer != null){
					writer.close();	//ここでIOExceptionは発生しない
				}
			}
		}
	}
}
このとき、Buffered〜系のクラスはclose処理でIOExceptionが発生する可能性がありますよね。
でも、BufferedWriterをPrintWriterでラップすると、close処理でIOExceptionがthrowされなくなります。
PrintWriterのclose内で良きに計らってくれていると信じていたのですが、よく考えると疑問で
PrintWriterのclose内でBufferedWriterがcloseされるとき、そこではIOExceptionが発生する可能性があるわけで、
発生した場合に何か例外処理したいとすると、どうコーディングすればいいのか疑問に思いました。

素直にBufferedWriterは専用の変数に格納して、明示的にclose→例外処理するしかないのでしょうか。
# これ以上try-catch増やしたくないのですが。。。

Re: PrintWriterのclose処理

Posted: 2015年2月14日(土) 19:02
by ISLe()
Java7以降で使えるtry-with-resources文というのがあります。
これを使うとtryブロックを抜けるときに自動的にcloseされます。

コード:

import java.io.*;
public class Copy {
	public static void main(String[] args) {
		try (
			BufferedReader reader = new BufferedReader(new FileReader("before.txt"));
			PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter("after.txt")));
		) {
			String line = null;
			while ((line = reader.readLine()) != null) {
				writer.println(line);
			}
		} catch (FileNotFoundException e) {
			/* 適宜例外処理 */
		} catch (IOException e) {
			/* 適宜例外処理 */
		}
	}
}

Re: PrintWriterのclose処理

Posted: 2015年2月14日(土) 23:46
by march3
ISLe() さんが書きました:Java7以降で使えるtry-with-resources文というのがあります。
これを使うとtryブロックを抜けるときに自動的にcloseされます。
はぁ、こんな便利構文できたんですね。
AutoCloseableを実装していれば使えるようで。。。

コード:

package sample;

import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (
           SampleWriter writer = new SampleWriter();
        ) {
            System.out.println("running...");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class SampleWriter implements AutoCloseable {
	@Override
	public void close() throws Exception {
		System.out.println("close exec now");
		throw new IOException("test close Exception");
	}
}
closeで例外発生してもcatchしますね。

コード:

running...
close exec now
java.io.IOException: test close Exception
	at sample.SampleWriter.close(Main.java:22)
	at sample.Main.main(Main.java:11)
ちなみに、自己解決になりますが、
PrintWriterはcheckError()メソッドというのがあり、
これでclose時にBufferedWriterなどのcloseで例外が発生したかどうかがわかるみたいです。
java.io.PrintWriterのソースをのぞくとこんな形になってました。。。

コード:

public class PrintWriter extends Writer {

中略

    /**
     * Closes the stream and releases any system resources associated
     * with it. Closing a previously closed stream has no effect.
     *
     * @see #checkError()
     */
    public void close() {
        try {
            synchronized (lock) {
                if (out == null)
                    return;
                out.close();
                out = null;
            }
        }
        catch (IOException x) {
            trouble = true;
        }
    }

中略

    /**
     * Flushes the stream if it's not closed and checks its error state.
     *
     * @return <code>true</code> if the print stream has encountered an error,
     *          either on the underlying output stream or during a format
     *          conversion.
     */
    public boolean checkError() {
        if (out != null) {
            flush();
        }
        if (out instanceof java.io.PrintWriter) {
            PrintWriter pw = (PrintWriter) out;
            return pw.checkError();
        } else if (psOut != null) {
            return psOut.checkError();
        }
        return trouble;
    }

以下略
try-catchでなくif文で判定することになりそう。

# 「Java 入出力」などでネット検索してヒットする参考サイトは
# 終了処理をテキトーに書いているものが大半なのはなぜなのか。。。(愚痴)