2016-06-16 19 views
4

はこのスニペットを考えてみましょう:Java:コンストラクタが失敗したオブジェクトはどうなりますか?

class Test1 { 
    private static Test1 instance; 
    @NonNull private final Date date1; 
    @NonNull private final Date date2; 

    Test1() throws Exception { 

     this.date1 = new Date(); 

     Test1.instance = this; 
     if (true) { 
      throw new Exception(); 
     } 

     this.date2 = new Date(); 
    } 

    public void dump() { 
     System.out.println("date1: " + date1); 
     System.out.println("date2: " + date2); 
    } 

    static void test() { 
     Test1 t1 = null; 
     try { 
      t1 = new Test1(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     Test1.instance.dump(); 
     assert t1 == null; 
    } 
} 

のTest1のコンストラクタは常に正しい静的フィールドに自分自身を割り当てた後、例外がスローされます。そのフィールドは部分的に初期化されたオブジェクトへの参照を保持します。date2フィールドはnullですが、@NonNullfinalと宣言されています。

test()関数は、生成されたt1への参照を直接取得できません。 catchブロックの後、t1はNULLです。 さらに、Test1.instanceは問題ありません。dump()関数を呼び出すと、date1が初期化されますが、date2nullになります。

ここでは何が起こっていますか?なぜ本当に違法な状態にあるオブジェクトへの参照を保持できますか? t1がnullであるという事実は、(In Java what happens when an object fails to be instantiated?とは違って)明らかにされていることを

EDIT 。この質問は、静的フィールドに格納されたオブジェクトの状態に関するものです。

+1

インスタンスを作成して初期化するのは別の作業です。インスタンスが最初に作成された後、そのインスタンスがコンストラクタによって初期化されます。オブジェクトが完全に初期化されていなくても、インスタンスはまだ存在します。これは* unsafe publication *の例です。あなたは(明らかに)それを避けるべきです。 –

+1

[Javaではオブジェクトのインスタンス化に失敗した場合どうしますか?](http://stackoverflow.com/questions/3421606/in-java-what-happens-when-an-object-fails-to-be - インスタンス化された) –

+0

@JulienLopez欺瞞ではなく、編集を参照してください。 – noamtm

答えて

8

は、以下のクラスのバイトコードを考えてみましょう:

class Foo { 
    public static void main(String[] args) { 
    new Foo(); 
    } 
} 

バイトコード:あなたはこのことから、新しいインスタンスの作成とコンストラクタの呼び出しを見ることができます

class Foo { 
    Foo(); 
    Code: 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: return 

    public static void main(java.lang.String[]); 
    Code: 
     0: new   #2     // class Foo 
     3: dup 
     4: invokespecial #3     // Method "<init>":()V 
     7: pop 
     8: return 
} 

が分離されている(ライン0 mainの4)。

したがって、完全に初期化されていない場合でも、インスタンスは存在します。そのインスタンスへの参照を別の参照に割り当てることができます。

完全初期化前にインスタンスを静的フィールドに割り当てることは、安全でない公開の例であり、(明らかに)回避する必要があります。

+0

私は、オブジェクトの参照を取得する他の方法(コンストラクタ内以外)は考えられません。これは、クラスの作成者がコンストラクタの最後の行の前に参照を保存することによって慎重でない場合にのみ発生する可能性があることを意味します(静的フィールドにオブジェクトの参照を保存することは奇妙です)。 – noamtm

+0

あなたがコンストラクタで行うことはすべてです。コンストラクタが終了すると、そのインスタンスは「完全に初期化されている」ため、そうでなければなりません。スレッドがコンテナを含むインスタンスを参照するコンストラクタ内のスレッドを開始することは、一般的な例です。また、 'this'をエイリアンメソッドに渡すと、その参照がどこに終わるかを一般に知ることができないので、それも安全でない刊行物です。 –

関連する問題