类的加载
类的加载是指将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在内存中创建一个java.lang.Class对象(HotSpot虚拟机将其放在方法区,其他虚拟机有自己的实现,并没有严格的规范说明Class对象位于哪里),用来封装类在方法区内的数据结构。
连接
-
验证:确保被加载类的正确性
确保被加载的字节码文件没有被恶意篡改,符合JVM对字节码的格式要求 -
准备:为类的静态变量分配内存,并将其初始化为默认值
此时将所有静态变量分配内存,赋值为JVM规定的默认值,如int赋值为0,String赋值为null; -
解析:把类中的符号引用转换为直接引用
在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用转换成直接引用的过程。
初始化
1.主动使用
所有Java虚拟机实现了每个Java类和接口只有被Java程序“首次主动使用”才会对其进行初始化,该初始化只能进行一次。
1.1 创建类的实例
如 Test test = new Test();
1.2 访问某个类或者接口的静态变量、对该静态变量赋值或者调用类的静态方法(助记符)
在JVM层面上,字节码对静态变量的访问借助助记符getstatic这样的一个指令进行处理,对静态变量赋值借助putstatic指令进行处理。而对调用类的静态方法则借助invokestatic指令进行处理,这三种指令都会导致类或者接口的初始化。
1.3 反射(如Class.forName(全限定类名))
Test test = (Test)Class.forName("com.xxx.otherTest").newInstance();
或者
Test test = Test.class.newInstance();
1.4 初始化一个类的子类
public class Test{
public static void main(String[] args){
System.out.println(Children.str);
}
}
class Parent{
public static String str = "Parent";
static{
System.out.println("Parent initial");
}
}
class Children extends Parent{
public static String str = "Children";
static{
System.out.println("Children initial");
}
}
输出结果:
对子类Children的初始化也会往上初始化Parent,若Parent往上还有其他类型,以此类推不断往上进行初始化。
1.5 Java虚拟机启动时被标明为启动类的类(包含Main方法的类)
1.6 JDK1.7开始提供的动态语言支持
java.lang.invoke.MethodHandle实例的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic句柄对应的类没有初始化,则初始化。
2.被动使用
除开以上7种情况外,其他对java类或者接口的使用都不会导致其初始化。
如:
public class Test{
public static void main(String[] args){
System.out.println(Children.str);
}
}
class Parent{
public static String str = "Parent";
static{
System.out.println("Parent initial");
}
}
class Children extends Parent{
static{
System.out.println("Children initial");
}
}
输出结果:
可以看出只有父类Parent被初始化,并没有对子类Children进行初始化,这是因为静态变量str是在父类中被定义的,虽然是子类Children对其进行调用,但是实际上是对父类的一个主动使用。
又或者:
public class Test{
public static void main(String[] args){
System.out.println(finalTest.str);
}
}
class finalTest{
public static final String str = "Parent";
static{
System.out.println("finalTest initial");
}
}
输出结果:
finalTest中定义的常量在编译期间就被放到Test调用该常量方法的常量池中(在编译器可以确定的常量值),本质上,调用类并没有直接引用到定义常量的类,因此不会触发定义常量类的初始化。
此外,还有对象数组的创建也不会导致类的初始化。
----------------------------------------------------分割线---------------------------------------------------------------
分类:JVM学习心得