加载 ----> 连接(验证,准备,解析*) ---->初始化
Java 虚拟机在首次主动使用类型时初始化它们。
主动使用(6种)
1、创建类的实例
2、调用类中声明的静态方法
3、操作类或者接口中声明的非常量静态字段
4、调用Java API 中特定的反射方法
5、初始化一个类的子类
6、指定一个类作为 Java 虚拟机启动的初始化类(main 方法)
使用一个非常量的静态字段只有当类或者接口的确声明了这个字段时才是主动使用。
被动使用
<a>
比如:
类中声明的字段可能会被子类引用;
接口中声明的字段可能会被子接口或者实现了这个接口的类引用
对于子类、子接口和实现了接口的类来说,这就是被动使用——使用它们并不会触发它们(子类、子接口和实现了接口的类)的初始化。
class NewParent{static int hoursOfSleep = (int)(Math.random() * 3.0 );static{System.out.println("NewParent was initialized.");}
}class NewbornBaby extends NewParent{static int hoursOfCrying = 6 + (int)(Math.random() * 2.0 );static{System.out.println("NewbornBaby was initialized.");}
}public class Example {static{System.out.println("Example was initialized.");}public static void main(String[] args) {int hours = NewbornBaby.hoursOfSleep;System.out.println(hours);}
}
输出为:
Example was initialized.
NewParent was initialized.
2
可见:类 Example 被初始化了,类NewParent 被初始化了。
虽然在使用中是用 NewbornBaby.hoursOfSleep ,但是 hoursOfSleep 是在父类中声明,所以子类 NewbornBaby 没有被初始化!
<b>
如果一个字段既是静态(static)的又是最终(final)的,并且使用一个编译时常量表达式初始化,使用这样的字段,就不是对声明该字段的类的主动使用。
Java 编译器把这样的字段解析成对常量的本地拷贝(该常量存在于引用者类的常量池或者字节码流中,或者二者都有)。
interface Angry{String greeting = "Grrrr!";int angerLevel = Dog.getAngerLevel();
}class Dog{static final String greeting = "Woof, woof, world!";static{System.out.println("Dog was initialiaed.");}static int getAngerLevel(){System.out.println("Angry was initialiaed");return 1;}
}public class Example2 {static{System.out.println("Example3 was initialized.");}public static void main(String[] args) {//Using Angry.greeting is a passive use of AngrySystem.out.println(Angry.greeting);//Using Dog.greeting is a passive use of DogSystem.out.println(Dog.greeting);System.out.println("----------------------");System.out.println(Angry.angerLevel);}
}
输出:
Example3 was initialized.
Grrrr!
Woof, woof, world!
----------------------
Dog was initialiaed.
Angry was initialiaed
1
由上可见: Angry.greenting 和 Dog.greeting 都是(static final + 编译时常量),使用时不会初始化类或接口(被动使用)!
而 Angry.angerLevel 虽然是 static final 但不是编译时常量,使用时会初始化类和接口(主动使用)!