为什么我们可以在加载类之前使用new运算符

时间:2014-04-23 13:06:55

标签: java class initialization

考虑下面的代码,它运行时不会抛出任何异常。

public class Test1 {
    public static int k = 0;
    public static Test1 t1 = new Test1("t1");
    public static Test1 t2 = new Test1("t2");
    public static int i = print("i");
    public static int n = 99;
    public int j = print("j");
    {
        print("constructor block");
    }

    static {
        print("static block");
    }

    public Test1(String str) {
        System.out.println((++k) + ":" + str + "    i=" + i + "    n=" + n);
        ++i;
        ++n;
    }

    public static int print(String str) {
        System.out.println((++k) + ":" + str + "    i=" + i + "    n=" + n);
        ++n;
        return ++i;
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Test1 t = new Test1("init");
    }

}

输出:

1:j    i=0    n=0
2:constructor block    i=1    n=1
3:t1    i=2    n=2
4:j    i=3    n=3
5:constructor block    i=4    n=4
6:t2    i=5    n=5
7:i    i=6    n=6
8:static block    i=7    n=99
9:j    i=8    n=100
10:constructor block    i=9    n=101
11:init    i=10    n=102

我用调试器逐步完成它。 可以看到clinit方法调用init方法。由于clinit方法仍然是类加载的一部分,这是否意味着我们可以在Test1类准备好之前实例化Test1对象?

1 个答案:

答案 0 :(得分:0)

我必须在规范中查找确切的定义(你也可以这样做),但让我们这样说:

假设您有以下内容:

class A {
  static B b = new B();
}

class B {
   static A a = new A(); 
}

如果在创建新实例之前必须完全初始化类,即必须运行所有静态初始化程序,则会出现死锁。不允许在静态块中创建新实例会严重限制语言,因此必须有一些方法来解决这个问题。

正如我所说,我必须要查看,但AFAIK的默认顺序是这样的:

  • 加载类并将静态变量初始化为其默认值
  • 因为构造函数被称为自上而下(即从超类到子类)
    • 首先按照定义的顺序调用静态初始化程序块(即初始化静态变量以及静态块)
    • 然后执行当前类的实例初始化程序块
    • 最后执行构造函数

当然,有一些方法可以解决这个问题,因为你已经发现但是通常不建议这样做,因为你最终可能会出现未定义/混乱的行为。一个这样的情况是调用超类构造函数中的一个方法,该方法在子类中被重写,并且访问子类的(尚未初始化的)字段。