类加载时机

jvm运行时通过将需要的class文件加载到方法区中,并对其进行验证、准备、解析和初始化,最终将二进制字节流转换成jvm方法区中的java类型。jvm加载class文件与其他语言加载二进制文件不一样,jvm加载是懒加载,只有在需要使用到该class文件时才会进行加载。既然如此,class文件该何时加载,加载过程中需要完成的工作有哪些?

1.类生命周期
这里写图片描述

以上步骤并非严格按照顺序执行,加载,验证,准备,初始化是严格按照顺序,但是为了支持动态绑定,解析动作会在初始化动作之后进行。


动态绑定知识补充:

动态绑定又称运行时绑定,根据具体对象类型决定调用哪个方法。

Son s = new Son();
s.method();

方法执行过程:

  1. 编译器根据对象的声明类型和方法名,搜索子类及其父类的方法表,找出所有访问属性为public的method方法。可能存在多个方法名为methd的方法,只是参数不同。
  2. 根据方法签名(方法名称+参数列表)找出完全匹配的方法。
  3. 如果方法为private、static、final或者构造器方法,则为静态调用
  4. 如果son定义了method方法,则直接调用,否则,搜索父类中是否存在method方法。

    动态绑定只是针对方法,对属性无效,因为属性无法被重写。

    public class Father{
        public int a = 1;
    } 
    
    public class Son extends Father{
        public int a = 2;
    }
    
    public class Main{
        public static void main(String[] args){
            Father f = new Son();
            System.out.println(f.a);
        }
    }

什么时候加载class文件,虚拟机规范并没有明确要求,只对何时需要初始化进行了要求,只有在一下5种情况下,类必须进行初始化,而加载,验证,准备必须在初始化之前就应该完成。

  • new实例化对象、读取/设置类的静态字段(静态常量除外,静态常量在完成准备阶段就可以获取)、执行静态方法。(静态字段的赋值操作是在初始化阶段执行clinit方法进行赋值)

  • 使用java.lang.reflect包的方法对类进行反射调用时,如果类没有初始化,需要对其进行初始化

  • 如果父类没有初始化,在初始化子类之前需要对父类进行初始化。

  • main方法所在的类

  • 动态语言相关

以上5种情况称为主动引用。引用类不会触发初始化的情况称为被动引用。

1. 子类引用父类静态字段,不会导致子类初始化。

public class Father{
    static{
        System.out.println("father");
    }
    public static int value = 2;
}

public class Son extends Father{
    static{
        System.out.println("son");
    }
}

public class Main{
    public static void main(String[] args){
        System.out.println(Son.a);
    }
}

//输出结果
father
2
//说明:class文件加载到jvm内存中,转换为方法区的数据结构,在解析阶段,
//对字段进行解析时,如果在类的方法区的字段信息中找不到对应的字段,那么
//通过接口、父类查找字段。在解析阶段可以完成的事,不用等到初始化阶段

2.通过数组定义引用类,不会触发此类的初始化

public class Main{
    public static void main(String[] args){
        Father[] f = new Father[10];
    }
}

//说明:没有输出Father类的初始化信息,会触发[Father的初始化阶段,创建
//动作由jvm完成

3.调用静态常量

public class Test{
    public static final int a = 1;
    static {
        System.out.println("test");
    }
}

public class Main{
    public static void main(String[] args){
        System.out.println(Test.a);
    }
}

//输出结果
1
//说明:没有输出Test的初始化消息,因为常量在准备阶段就已经放入到方法
//区的字段信息中

总结:对类的生命周期做了一个简单的描述,以及何时对类进行初始化,主动引用、被动引用做了一个简单总结。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值