2016-06-16 3 views
4

Betrachten Sie diesen Code-Schnipsel:Java: Was passiert mit einem Objekt, dessen Konstruktor fehlgeschlagen ist?

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 Konstruktor wirft immer eine Ausnahme gleich nach mir zu einem statischen Feld zuweisen. Dieses Feld behält einen Verweis auf ein teilweise initialisiertes Objekt: Ein Objekt, das date2 ist, ist null, obwohl es @NonNull und final deklariert ist.

Die Funktion test() kann nicht direkt eine Referenz auf das generierte t1 erhalten. Nach dem catch Block ist T1 Null. Und noch, Test1.instance ist nur in Ordnung, und Aufruf seiner dump() Funktion zeigt, dass date1 ist initialisiert, aber date2 ist null.

Was geht hier vor? Warum kann ich einen Verweis auf ein Objekt behalten, das sich wirklich in einem unzulässigen Zustand befindet?

EDIT Die Tatsache, dass t1 null ist offensichtlich, ist (im Gegensatz zu In Java what happens when an object fails to be instantiated?). Diese Frage bezieht sich auf den Status des Objekts, das im statischen Feld gespeichert werden konnte.

+1

Das Erstellen und Initialisieren der Instanz sind separate Dinge: Die Instanz wird zuerst erstellt und dann über ihren Konstruktor initialisiert; Selbst wenn das Objekt nicht vollständig initialisiert ist, existiert immer noch eine Instanz. Dies ist ein Beispiel für eine unsichere Veröffentlichung *, die Sie (offensichtlich) vermeiden sollten. –

+1

Mögliches Duplikat von [In Java, was passiert, wenn ein Objekt nicht instanziiert werden kann?] (Http://stackoverflow.com/questions/3421606/in-java-what-happens-when-an-object-fails-to-be -instantiiert) –

+0

@JulienLopez nicht ein Betrogener, bitte siehe bearbeiten. – noamtm

Antwort

8

Betrachten Sie den Bytecode für die folgende Klasse:

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

Bytecode:

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 
} 

Sie können von dieser dieser Schöpfung der neuen Instanz und der Aufruf des Konstruktors sehen getrennt sind (Zeilen 0 und 4 in main).

Also, auch wenn es nicht vollständig initialisiert ist, existiert die Instanz; und Sie können einen Verweis auf diese Instanz einer anderen Referenz zuweisen.

Das Zuweisen der Instanz zu einem statischen Feld, bevor es vollständig initialisiert wird, ist ein Beispiel unsafe Publikation, und Sie sollten es (offensichtlich) vermeiden.

+0

Ich kann mir keinen anderen Weg vorstellen (außer innerhalb des Konstruktors), die Referenz dieses Objekts zu bekommen. Dies bedeutet, dass dies nur passieren kann, wenn der Autor der Klasse nicht vorsichtig ist, indem der Verweis vor der letzten Zeile des Konstruktors gespeichert wird (ignorieren Sie die Tatsache, dass das Speichern eines Objektverweises in einem statischen Feld auf diese Weise merkwürdig ist). – noamtm

+0

Ja, es geht nur darum, was Sie im Konstruktor machen; Es muss sein, denn sobald der Konstruktor fertig ist, ist die Instanz "vollständig initialisiert". Das Starten eines Threads im Konstruktor, in dem der Thread auf die enthaltene Instanz verweist, ist ein gängiges Beispiel. Auch wenn Sie "this" an eine Alien-Methode übergeben, können Sie im Allgemeinen nicht wissen, wo diese Referenz endet, so dass auch die Veröffentlichung unsicher ist. –