浅谈Java父子类加载顺序
在Java中,每个类都需要被加载才能被使用。这个过程中,父类和子类的加载顺序会对最终的运行结果产生影响。下面我们来详细讲解Java父子类加载顺序的完整攻略。
1. Java类加载机制
Java类加载器按照从上往下、从父到子的顺序,依次进行类加载:
- 引导类加载器:JVM自带的类加载器,主要用来加载Java核心库,比如
rt.jar
等。 - 扩展类加载器(Extension ClassLoader):加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的jar包,也可以用java.ext.dirs系统变量指定。
- 应用程序类加载器(Application ClassLoader):加载当前应用的classpath下所有的类。
- 自定义类加载器:用户自己定义的类加载器。
类的加载过程包括加载、链接、初始化三个步骤:
- 加载(Loading):查找并加载类的二进制数据(
.class
文件)。加载阶段需要完成以下三件事情: - 通过一个类的全限定名来获取定义该类的二进制字节流。
- 将该字节流所代表的静态存储结构转换为方法区的运行时数据结构。
- 在Java堆中生成一个代表该类的
java.lang.Class
对象,作为方法区这个类的各种数据的访问入口。 - 链接(Linking):将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证(Verification):确保被加载的类的正确性。
- 准备(Preparation):为类的静态变量分配内存,并将其初始化为默认值。
- 解析(Resolution):把类中的符号引用转换为直接引用。
- 初始化(Initialization):为类的静态变量赋予正确的初始值。
2. Java父子类加载顺序
Java虚拟机会根据类的全限定名称进行类加载,当在父和子类中都找到同一个类时,父子类加载的顺序会对最终的运行结果产生影响,具体可以分为以下两种情况:
2.1 子类中引用父类的静态变量
当子类引用父类的静态变量时,Java虚拟机会先加载父类的静态变量,再加载子类的静态变量。示例如下:
public class Parent {
public static String name = "Parent";
static {
System.out.println("Parent static block");
}
}
public class Child extends Parent {
public static String name = "Child";
static {
System.out.println("Child static block");
}
}
public class Test {
public static void main(String[] args) {
System.out.println(Child.name);
}
}
输出结果:
Parent static block
Child static block
Child
可以看到,先输出父类的静态代码块,再输出子类的静态代码块,最后输出子类的静态变量值。
2.2 子类中重写父类的方法
当子类重写父类的方法时,Java虚拟机会优先加载子类的方法。示例如下:
public class Parent {
public void sayHello() {
System.out.println("Hello, Parent!");
}
}
public class Child extends Parent {
public void sayHello() {
System.out.println("Hello, Child!");
}
}
public class Test {
public static void main(String[] args) {
Parent p = new Child();
p.sayHello();
}
}
输出结果:
Hello, Child!
可以看到,创建子类对象时,会先加载子类的方法,如果子类中重写了父类的方法,则会调用子类的方法,而不是父类的方法。
3. 总结
Java类加载顺序按照从上到下、从父到子的顺序依次加载。当父子类中都含有同一个类时,父子类的加载顺序会影响最终的运行结果。子类的静态变量和父类的静态变量的加载顺序为:先加载父类的静态变量,再加载子类的静态变量。子类重写父类的方法时,Java虚拟机会优先加载子类的方法。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Java父子类加载顺序 - Python技术站