前言
在讲解前先来回顾下java jvm相关的基础知识以便好理解。
类加载过程
类加载是指将类的.class文件中的二进制数据读入到内存中,放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 类的加载分为以下几个主要阶段:
-
加载(Loading)
- 查找并加载类的二进制数据。
- 一般是指将.class文件读入内存,并为之创建一个java.lang.Class对象。
-
链接(Linking)
- 验证:确保被加载类的正确性。
- 准备:为类的静态变量分配内存,并将其初始化为默认值。
- 解析:将类中的符号引用转换为直接引用。
-
初始化(Initialization)
- 为类的静态变量赋予正确的初始值。
- 声明类变量是指定初始值
类加载的触发条件主要有以下几种:
- 创建类的实例。
- 访问类的静态方法。
- 使用类或接口的静态字段。
- 反射(如Class.forName("com.example.MyClass"))。
- 初始化一个类的子类(首先会初始化父类)。
- Java虚拟机启动时被标明为启动类的类(包含main方法的那个类)。
类加载器有以下几种类型:
-
引导类加载器(Bootstrap Class Loader)
- 加载Java的核心库(JAVA_HOME/jre/lib/rt.jar中的类或者-Xbootclasspath参数指定的路径中的类)。
-
扩展类加载器(Extension Class Loader)
- 加载JAVA_HOME/jre/lib/ext目录中或者由java.ext.dirs系统变量指定的路径中的类库。
-
系统类加载器(System/Application Class Loader)
- 加载系统类路径(Classpath)上指定的类库。
父子类调用顺序
-
静态代码块和静态字段初始化(按照声明顺序):
- 首先执行父类的静态代码块和静态字段初始化,然后执行子类的静态代码块和静态字段初始化。
-
父类的成员变量和实例初始化块(按照声明顺序):
- 当创建子类的实例时,首先初始化父类的成员变量和实例初始化块。
-
父类的构造器:
- 在父类的成员变量和实例初始化块初始化之后,调用父类的构造器。
-
子类的成员变量和实例初始化块(按照声明顺序):
- 父类的构造器完成后,初始化子类的成员变量和实例初始化块。
-
子类的构造器:
- 在子类的成员变量和实例初始化块初始化之后,调用子类的构造器。
一段代码深入回顾 jvm的执行顺序
/** Java 一段代码讲解java的继承、构造函数调用顺和属性覆盖
* @Author: andy
* @Date: 2024/7/11 10:15
* @Description:
**/
public class JVMSsubLClass {
static class Father {
public int i = 1;
public Father() {
Class<? extends Father> clz = this.getClass();
i = 2;
show();
}
public void show() {System.out.println("Father i =" + i);}
}
static class Son extends Father {
public int i = 3;
public Son(){
i =4;
show();
}
@Override
public void show() {
System.out.println("Son i =" + i);
System.out.println("Son super.i =" + super.i);
}
}
public static void main(String[] args) {
Father f =new Son();
System.out.println(f.i);
}
}
输出结果
Son i =0
Son i =4
2
main方法中创建Son类的实例。- 在创建
Son的实例之前,首先需要调用父类Father的构造方法。 Father的构造方法设置i的值为2。- 然后
Father的构造方法调用show方法。由于show方法在Son类中被覆盖,因此实际上调用的是Son类的show方法。 Son类的show方法尝试打印i的值。但由于Son类的构造方法还没有执行,i的值还是其默认值0。Father的构造方法完成后,Son的构造方法开始执行。Son的构造方法设置i的值为4,并再次调用show方法。- 这一次,
Son类的show方法打印的是i的当前值4,以及继承自Father类的i值2。 - 最后,当在
main方法中打印f.i时,由于f是以Father类型引用的Son对象,所以它访问的是Father类中定义的i字段,其值为2。