7

ちょっと驚いたことに、次のコードは "Close" を 2 回出力します。デバッガーを実行すると、 がMyPrintStream.close()呼び出されたように見えますがsuper.close()、これが再び呼び出さMyPrintStream.close()れます。

    
import java.io.*;

public class PrintTest
{
    static class MyPrintStream extends PrintStream
    {
        MyPrintStream(OutputStream os)
        {
            super(os);
        }

        @Override
        public void close()
        {
            System.out.println("Close");
            super.close();
        }
    }

    public static void main(String[] args) throws IOException
    {
        PrintStream ps = new MyPrintStream(new FileOutputStream(File.createTempFile("temp", "file")));
        ps.println("Hello");
        ps.close();
    }
}

なぜこうなった?PrintStream を拡張するべきではありませんか?

4

2 に答える 2

11

デバッガーでコードを見て、メソッドにブレークポイントを設定すると、がメソッドを呼び出してclose()いるかのスタック トレースが明らかになります。close()

  1. あなたの主な方法
  2. sun.nio.cs.StreamEncoder$CharsetSE.implClose() 431行目

後者の完全なスタック トレースは次のようになります。

PrintTest$MyPrintStream.close() line: 20    
sun.nio.cs.StreamEncoder$CharsetSE.implClose() line: 431 [local variables unavailable]  
sun.nio.cs.StreamEncoder$CharsetSE(sun.nio.cs.StreamEncoder).close() line: 160 [local variables unavailable]    
java.io.OutputStreamWriter.close() line: 222 [local variables unavailable]  
java.io.BufferedWriter.close() line: 250 [local variables unavailable]  
PrintTest$MyPrintStream(java.io.PrintStream).close() line: 307  
PrintTest$MyPrintStream.close() line: 20    
PrintTest.main(java.lang.String[]) line: 27 

悲しいことに、私のIDEにはsun.nio.cs.StreamEncoderのソース添付ファイルがないため、StreamEncoderがPrintStreamにコールバックする理由はわかりません:(問題がある場合、これはJDK 6です。

ところで、close()メソッド内のカスタム コードが 2 回実行されていることに気付いたためにこの質問をしている場合は、 this.closing. PrintStream.close()これを true (およびクラスのコメント state /* To avoid recursive closing */) に設定します。

于 2009-05-12T13:47:41.287 に答える
1

PrintStream のソースを見てください。

基礎となる Writer への 2 つの参照textOutcharOut1 つは文字ベース、もう 1 つはテキストベース (意味は何でも) を持っています。また、バイトベースの OutputStream への 3 番目の参照を継承しますout

/**
 * Track both the text- and character-output streams, so that their buffers
 * can be flushed without flushing the entire stream.
 */
private BufferedWriter textOut;
private OutputStreamWriter charOut;

メソッドではclose()、それらすべてを閉じます(textOut基本的には と同じcharOutです)。

 private boolean closing = false; /* To avoid recursive closing */

/**
 * Close the stream.  This is done by flushing the stream and then closing
 * the underlying output stream.
 *
 * @see        java.io.OutputStream#close()
 */
public void close() {
synchronized (this) {
    if (! closing) {
    closing = true;
    try {
        textOut.close();
        out.close();
    }
    catch (IOException x) {
        trouble = true;
    }
    textOut = null;
    charOut = null;
    out = null;
    }
}
}

ここで興味深いのは、charOut には、PrintStream 自体を参照する (ラップされた) が含まれていることです (init(new OutputStreamWriter(this))コンストラクターの に注意してください)。

private void init(OutputStreamWriter osw) {
   this.charOut = osw;
   this.textOut = new BufferedWriter(osw);
}

/**
 * Create a new print stream.
 *
 * @param  out        The output stream to which values and objects will be
 *                    printed
 * @param  autoFlush  A boolean; if true, the output buffer will be flushed
 *                    whenever a byte array is written, one of the
 *                    <code>println</code> methods is invoked, or a newline
 *                    character or byte (<code>'\n'</code>) is written
 *
 * @see java.io.PrintWriter#PrintWriter(java.io.OutputStream, boolean)
 */
public PrintStream(OutputStream out, boolean autoFlush) {
this(autoFlush, out);
init(new OutputStreamWriter(this));
}

そのため、 への呼び出しはclose()を呼び出しcharOut.close()、それが再びオリジナルを呼び出します。close()これが、無限再帰を短くするための終了フラグを持っている理由です。

于 2009-05-12T22:41:19.700 に答える