你知道 Java 类什么情况下会被初始化吗?
加载阶段是开发期可控性最强的阶段,我们可以使用系统的类加载器去完成,也可以使用自定义的加载器去完成。
2.连接阶段
在这一阶段我们我们主要做了下面几件事:
1.验证我们的 Class 文件中的字节流是否符合我们虚拟机的要求,并且不会危害虚拟机的自身安全。
2.为我们的类变量分配内存并设置类变量的初始值。(类变量是我们 static 修饰的变量,不要和不是 static 修饰的成员属性(实例变量)混淆。)
还要注意一点就是设置类变量的初始值,不等同于我们的赋值操作。
比如 static int a = 2;这个阶段完成以后,我们的 a 的初始值为 0,而非 2(被 final 修饰的常量除外)。这个我们后面说到类的初始化的时候再讲。
3.将常量池的符号引用替换为直接引用(符号引用,引用的目标并不一定以及加载到内存,直接用引用,目标一定已经被加载到内存)。
3.初始化阶段
在初始化我们做的主要事情就是初始化我们的类变量和我们在程序中编写的初始化操作。上面我们在连接阶段提到的 a 被初始化为零值,在我们的初始化阶段我们为我们的类变量进行赋值操作。
虚拟机严格规定了有且只有四种情况必须立即对类进行初始化。
1.遇到 new ,getstatic,putstatic,invokestatic 这四条字节码指令时。
new 就是我们所熟悉的在实例化对象的时候,getstatic,putstatic,invokestatic 是什么呢?就是我们在获取和设置一个 static 变量(final 修饰的类变量除外,final 修饰的类变量,在编译期已经把结果放入常量池。)时,还有我们在调用我们类的静态方法的时候。如果类没有被初始化,必须立即对类进行初始化。
2.在我们使用反射对类进行发射调用的时候。
3.当我们初始化一个类的时候,发现其父类没有初始化,必须先对其父类进行初始化。
4.当虚拟机启动时,我们的主类会被初始化(包含 main()方法的那个类)。
下面我们通过我们的代码来验证以下,类的初始化过程。
除了上面的四种情况,其他任何一种情况,都不会触发类的初始化操作。
下面我们来举个例子验证以下,我们上面所说的几种情况:
我们先定义两个类:一个 Person 类和 Person 类的子类 Person 类。
public class Person {
public static int a = 10;
public static final int b = 10;
static {
System.out.print("person 类被初始化了。");
}
}
public class Chi
ld extends Person{
static {
System.out.print("Child 类被初始化了。");
}
public static int addNum(int a ,int b){
return a+b;
}
}
先运行我们的程序,发现控制台并没有打印任何输出,我们的两个类都没有被初始化。
1.调用 new Person()实例化一个 Person 对象,我们来看看控制台的输出结果:
2.调用 Person.a 访问 Person 类的变量 a,我们看下控制台输出。
评论