Java:构造函数失败的对象会发生什么?

noa*_*mtm 4 java null constructor exception

请考虑以下代码段:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Test1的构造函数在将自身分配给静态字段后总是抛出异常.那场保持对部分初始化对象的引用的对象是谁的date2领域是null,即使它宣布@NonNullfinal.

test()函数不能直接获取对生成的t1的引用.在catch块之后,t1为空.然而,Test1.instance只是罚款,并调用其dump()功能显示date1初始化,但是date2null.

这里发生了什么?为什么我可以保持对真正处于非法状态的对象的引用?

编辑 那个t1为null的事实很明显(与Java不同,当一个对象无法实例化时会发生什么?).这个问题是关于设法存储在静态字段中的对象的状态.

And*_*ner 8

考虑以下类的字节码:

class Foo {
  public static void main(String[] args) {
    new Foo();
  }
}
Run Code Online (Sandbox Code Playgroud)

字节码:

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
}
Run Code Online (Sandbox Code Playgroud)

您可以从中看到,新实例的创建和构造函数的调用是分开的(第0行和第4行main).

因此,即使它没有完全初始化,实例也存在; 并且您可以将该实例的引用分配给另一个引用.

在完全初始化之前将实例分配给静态字段是不安全发布的一个示例,您应该(显然)避免它.