2009-05-12 25 views
7

驚いたことに、次のコードは「閉じる」を2回印刷します。デバッガを実行すると、MyPrintStream.close()super.close()となり、もう一度MyPrintStream.close()と呼びます。PrintStream.close()はなぜ2回呼び出されるのですか?

 
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を拡張してはいけませんか?

+0

これはデバッガに適したものです。 closeメソッドにブレークポイントを置くと、なぜ呼び出されたのかを知ることができます。 –

答えて

1

PrintStreamのソースをご覧ください。

基本的なWriter textOutcharOutの2つの参照、1つの文字ベース、1つのテキストベース(その意味を問わず)があります。また、それはoutと呼ばれるバイトベースのOutputStreamへの3番目の参照を継承します。それはそれらのすべてを閉鎖close()方法において

/** 
* 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; 

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は(ラップ)

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()を呼び出しますのPrintStream自体(注意コンストラクタでinit(new OutputStreamWriter(this)))を基準に含まれていることですこれは元のclose()を再度呼び出します。そのため、無限再帰を短くする閉じるフラグがあります。

11

デバッガであなたのコードを見て、close()方法でブレークポイントを設定した場合、それはがあなたのclose()メソッド呼び出しているのスタックトレースを明らかます:

  1. あなたの主な方法
  2. をsun.nio.cs.StreamEncoder $ CharsetSE.implClose()ライン431

ような後者のルックスのための完全なスタックトレース:

私のIDEがsun.nio.cs.StreamEncoderのソース添付ファイルを持っていないよう
StreamEncoderが、しかし戻ってあなたのPrintStreamにコールする理由:(これはJDK 6ですところで、私は を伝えることができないのに悲しい10
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 

それが重要であれば。道ことで

は、あなたがこの質問をしている場合は、あなたのclose()方法でカスタムコードは、あなたが本当にthis.closingかどうかをチェックしなければならない二回実行されていることに気づいたので。 PrintStream.close()はこれをtrueに設定します(クラスのコメント状態は/* To avoid recursive closing */です)。

+1

+1これから自分自身でこれを解決する方法を教えてください –

+1

'closing'インスタンス変数はPrintStreamにとってプライベートなので、私はそれを確認することはできません。 –

+2

jdkにはいくつかの似たようないくつかのクラスがあります。クラスAとクラスBがお互いに参照する状況があり、ユーザーがAまたはBのいずれかへの参照を持つ可能性があり、そのうちのいずれかを閉じてもう一方を閉じなければならないと考えています。 前述したように、通常、複数の呼び出しからクローズメソッドを保護する必要があります(再帰呼び出しは、より狡猾ではありません)。 – james

関連する問題