# 类加载机制

# 类加载过程

  1. # 加载
    • 通过一个类的全限定名获取定义此类的二进制字节流;比如Class.forName("全限定名")
    • 将这个字节流代表的静态存储结构转化为方法区的运行时数据结构;
    • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
  2. # 链接
    • 验证(Verify)

      • 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全;

      • 主要包括四种验证:文件格式验证、元数据验证、字节码验证、符号引用验证;

    • 准备(Prepare)

      • 为类变量分配内存并且设置该类变量的默认初始值;
      • 这里不包含final修饰的static,因为final在编译的时候就会分配了,准备阶段会显示初始化;
      • 这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到堆中;
    • 解析

      • 将常量池内的符号引用转换为直接引用的过程
      • 例如静态代码块、静态变量的显示赋值
      • 事实上,解析操作往往会伴随着JVM在执行完初始化之后在执行
      • 符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《Java虚拟机规范》的Class文件格式中。直接引用就是指- 向目标的指针、相对偏移量或一个间接定位到目标的句柄
      • 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对常量池中的CONSTANT_Filedref_info、CONSTANT_Class_info、CONSTANT_Methodref_info等。
  3. # 初始化
    • 初始化阶段就是执行类构造器方法的过程;
    • 此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。;
    • 构造器方法中指令按语句在源文件中出现的顺序执行;
    • 类构造器方法不同于类的构造器。构造器是虚拟机视角下的类构造器方法;
    • 若该类具有父类,JVM会保证子类的构造器方法执行前,父类的类构造器方法已经执行完毕;
    • 虚拟机必须保证一个类的类构造器方法在多线程下被同步加锁;

# 双亲委托

public class Demo {
    public static void main(String[] args) {
        Student1 student = new Student1();
        System.out.println("new Student() 完毕,开始调用getStudentId()方法");
        // 打印出来的值是100
        System.out.println("#推测~~打印出来的值是100");
        student.getStudentId();
    }
}

class Person1 {

    private int personId;

    public Person1() {
        // 1、第一步,走父类无参构造函数
        System.out.println("第一步,走父类无参构造函数");
        System.out.println("");
        setId(100);
    }

    public void setId(int id) {
        System.out.println("第三步,通过super.setId(id);走父类发方法~~~id="+id);
        personId = id;
        System.out.println("在父类:studentId 被赋值为 " + personId);
        System.out.println("");
    }
}
class Student1 extends Person1 {

    private int studentId = 1;

    public Student1() {
        System.out.println("第五步,在走子类无参构造函数前,会先执行子类的普通成员变量初始化");
        System.out.println("第六步,走子类无参构造函数");
        System.out.println("");
    }

    @Override
    public void setId(int id) {
        System.out.println("第二步,走子类方法~~id="+id);
        // 3、第三步,走子类方法
        super.setId(id);
        studentId = id;
        System.out.println("第四步,再回此方法,在子类:studentId 被赋值为 " + studentId);
        System.out.println("");
    }

    public void getStudentId() {
        System.out.println("第七步,走getStudentId()");
        System.out.println("studentId = " + studentId);
        System.out.println("");
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55