标签:阶段 默认值 http jpg 方法 准备 子类初始化 访问 static
全限定名 : 包名 + 类名
加载 : 1. 根据类的全限定名找到对应的二进制流
2. 将静态数据结构转换为方法区运行时数据结构
3. 在堆中生成一个java.lang.class对象,作为访问方法区这些数据的入口
验证 : 校验字节码正确性 和 二进制流正确性等,其目的就是为了保证jvm的正确运行.
准备 : 为类中的静态变量准备分配内存,并赋予其默认值
如 int a = 5 . Integer b = 10 . 在这里 a =0 , b =null . 在真正初始化的时候才会为其赋予真正的值
常量的初值即为代码中设置的值,final static tmp = 456, 那么该阶段tmp的初值就是456
解析 : 将类中的符号引用转换为直接引用
符号引用 : 一个字符串,但是这个字符串给出了可以唯一识别一个类,方法,变量的相关信息
直接引用 : 可以理解为内存地址或者偏移量 . 当为一个类变量或者类方法时, 直接引用代表指向方法区的一个指针 . 当为实例时,直接引用代表 从这个实例的头指针算起,到这个实例位置的偏移量.
在此阶段,虚拟机会把次类中的方法名,变量名,类名这些符号引用转换为直接引用.
初始化 : 执行构造器 <client>方法 . 换句话说, 这个时候只对 类变量或者静态代码块进行初始化
如 上面的 a此时值为5 , b = 10
如果存在多个静态变量或者静态代码块,从上至下顺序执行.
此阶段,如果发现此类存在父类,且父类未进行初始化,则优先初始化父类
1. 直接new此对象的实例
2. 对一个类的静态变量进行读取或者重新赋值
3. 虚拟机启动时, 把此类作为执行的主类
4. 通过java.lang.reflect 对此类进行反射 (其实反射好像也是通过调用构造方法来初始化得到的对象)
5. 初始化一个类的子类
//父类
public class SuperClass {
//静态变量value
public static int value = 666;
//静态块,父类初始化时会调用
static{
System.out.println("父类初始化!");
}
}
//子类
public class SubClass extends SuperClass{
//静态块,子类初始化时会调用
static{
System.out.println("子类初始化!");
}
}
//主类、测试类
public class NotInit {
public static void main(String[] args){
System.out.println(SubClass.value);
}
}
输出 : 父类初始化! 666
1. 静态常量在编译阶段就会被存入调用类的常量池中,不会引用到定义常量的类,这是一个特例,需要特别记忆,不会触发类的初始化!
//父类
public class SuperClass {
//静态变量value
public static int value = 666;
//静态块,父类初始化时会调用
static{
System.out.println("父类初始化!");
}
}
//主类、测试类
public class NotInit {
public static void main(String[] args){
SuperClass[] test = new SuperClass[10];
}
}
输出 : 什么也没有
2. 通过数组来引用类,不会触发类的初始化,因为是数组new,而类没有被new
标签:阶段 默认值 http jpg 方法 准备 子类初始化 访问 static
原文地址:https://www.cnblogs.com/liweibing/p/12661773.html