Java虚拟机JVM学习04 类的初始化
类的初始化
在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。java
在程序中,静态变量的初始化有两种途径:dom
1.在静态变量的声明处进行初始化;学习
2.在静态代码块中进行初始化。spa
没有通过显式初始化的静态变量将原有的值。code
一个比较奇怪的例子:视频
packagecom.mengdd.classloader;classSingleton {//private static Singleton mInstance = new Singleton();//位置1//位置1输出://counter1: 1//counter2: 0
public static intcounter1;public static int counter2 = 0;private static Singleton mInstance = new Singleton();//位置2//位置2输出://counter1: 1//counter2: 1
privateSingleton() {
counter1++;
counter2++;
}public staticSingleton getInstantce() {returnmInstance;
}
}public classTest1 {public static voidmain(String[] args) {
Singleton singleton=Singleton.getInstantce();
System.out.println("counter1: " +Singleton.counter1);
System.out.println("counter2: " +Singleton.counter2);
}
}
可见将生成对象的语句放在两个位置,输出是不同的(相应位置的输出已在程序注释中标明)。对象
这是由于初始化语句是按照顺序来执行的。blog
静态变量的声明语句,以及静态代码块都被看作类的初始化语句,Java虚拟机会按照初始化语句在类文件中的前后顺序来依次执行它们。教程
类的初始化步骤
1.假如这个类尚未被加载和链接,那就先进行加载和链接。接口
2.假如类存在直接的父类,而且这个父类尚未被初始化,那就先初始化直接的父类。
3.假如类中存在初始化语句,那就依次执行这些初始化语句。
类的初始化时机
Java程序对类的使用方式能够分为两种:
1.主动使用
2.被动使用
全部的Java虚拟机实现必须在每一个类或接口被Java程序首次主动使用时才初始化它们。
主动使用的六种状况:
1.建立类的实例。
new Test();
2.访问某个类或接口的静态变量,或者对该静态变量赋值。
int b =Test.a;
Test.a= b;
3.调用类的静态方法
Test.doSomething();
4.反射
Class.forName(“com.mengdd.Test”);
5.初始化一个类的子类
classParent{
}class Child extendsParent{public static int a = 3;
}
Child.a= 4;
6.Java虚拟机启动时被标明为启动类的类
java com.mengdd.Test
除了以上六种状况,其余使用Java类的方式都被看做是对类的被动使用,都不会致使类的初始化。
接口的特殊性
当Java虚拟机初始化一个类时,要求它的全部父类都已经被初始化,可是这条规则并不适用于接口。
在初始化一个类时,并不会先初始化它所实现的接口。
在初始化一个接口时,并不会先初始化它的父接口。
所以,一个父接口并不会由于它的子接口或者实现类的初始化而初始化,只有当程序首次使用特定接口的静态变量时,才会致使该接口的初始化。
final类型的静态变量
final类型的静态变量是编译时常量仍是变量,会影响初始化语句块的执行。
若是一个静态变量的值是一个编译时的常量,就不会对类型进行初始化(类的static块不执行);
若是一个静态变量的值是一个非编译时的常量,即只有运行时会有肯定的初始化值,则就会对这个类型进行初始化(类的static块执行)。
例子代码:
packagecom.mengdd.classloader;importjava.util.Random;classFinalTest1 {public static final int x = 6 / 3; //编译时期已经可知其值为2,是常量//类型不须要进行初始化
static{
System.out.println("static block in FinalTest1");//此段语句不会被执行,即无输出
}
}classFinalTest2 {public static final int x = new Random().nextInt(100);//只有运行时才能获得值
static{
System.out.println("static block in FinalTest2");//会进行类的初始化,即静态语句块会执行,有输出
}
}public classInitTest {public static voidmain(String[] args) {
System.out.println("FinalTest1: " +FinalTest1.x);
System.out.println("FinalTest2: " +FinalTest2.x);
}
}
主动使用的归属明确性
只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才能够认为是对类或接口的主动使用。
packagecom.mengdd.classloader;classParent {static int a = 3;static{
System.out.println("Parent static block");
}static voiddoSomething() {
System.out.println("do something");
}
}class Child extendsParent {static{
System.out.println("Child static block");
}
}public classParentTest {public static voidmain(String[] args) {
System.out.println("Child.a: " +Child.a);
Child.doSomething();//Child类的静态代码块没有执行,说明Child类没有初始化//这是由于主动使用的变量和方法都是定义在Parent类中的
}
}
ClassLoader类
调用ClassLoader类的loadClass()方法加载一个类,并非对类的主动使用,不会致使类的初始化。
packagecom.mengdd.classloader;classCL {static{
System.out.println("static block in CL");
}
}public classClassLoaderInitTest {public static void main(String[] args) throwsException {
ClassLoader loader=ClassLoader.getSystemClassLoader();
Class> clazz = loader.loadClass("com.mengdd.classloader.CL");//loadClass方法加载一个类,并非对类的主动使用,不会致使类的初始化
System.out.println("----------------");
clazz= Class.forName("com.mengdd.classloader.CL");
}
}
参考资料
圣思园张龙老师Java SE系列视频教程。