Java “;很少有程序员知道这样一个事实:类';s构造函数和方法可以在初始化之前运行”;

Java “;很少有程序员知道这样一个事实:类';s构造函数和方法可以在初始化之前运行”;,java,initialization,Java,Initialization,官方Java指南中指出 (本页最后一段) 很少有程序员知道,类的构造函数和方法可以在初始化之前运行。发生这种情况时,很可能类的不变量尚未建立,这可能会导致严重而微妙的错误 这是什么意思?这是什么时候发生的?这是我在日常使用Java时必须关心的问题吗?基本上,他们讨论了以下情况: public class Foo { public static Foo INSTANCE = new Foo(); // Prints null public static String s = "b

官方Java指南中指出 (本页最后一段)

很少有程序员知道,类的构造函数和方法可以在初始化之前运行。发生这种情况时,很可能类的不变量尚未建立,这可能会导致严重而微妙的错误


这是什么意思?这是什么时候发生的?这是我在日常使用Java时必须关心的问题吗?

基本上,他们讨论了以下情况:

public class Foo {
    public static Foo INSTANCE = new Foo(); // Prints null

    public static String s = "bar";

    public Foo() {
        System.out.println(s);
    }
}
如您所见,在本例中,构造函数在静态字段
s
的初始值设定项之前运行,即。E在类的完全初始化之前。这只是一个简单的示例,但当涉及多个类时,它可能会变得更复杂


这并不是你在日常工作中经常看到的事情,但是你需要意识到这种可能性,并在编写代码时避免它

> P>作为一个例子,考虑构造函数中的虚拟方法调度。
class Foo {
   Foo() {
      int a = bar();        
      b = 7;
   }

   private int b;

   protected int baz() { assert b == 7; return b; } ;

   protected abstract int bar();
}

如果一个子类碰巧从他们的
bar
实现中调用
baz
,他们就会点击断言。对象尚未完成构造,因此
Foo
基类处于某种状态。

我认为它们是指逻辑初始化。例如,类A有一个方法
init()
,必须在使用任何业务方法之前调用该方法。但其他使用该类的程序员并没有阅读手册,也并没有编写新的A().foo()。在这种情况下,
foo()
可能无法正常工作。在这种情况下,断言可能很有用。您可以在开头检查未调用
init()
,并抛出断言

构造函数也是如此。当有人扩展您的类A时,可能会发生这种情况:

class B extends A {
    B() {
        foo(); // init() must be called before foo!
    }
}

什么是逻辑初始化(与仅初始化不同)?