Java虚拟机的类加载机制

本文深入探讨Java虚拟机(JVM)的类加载机制,包括加载、连接和初始化过程,以及类加载器的角色和双亲委派模型。文章还讨论了主动使用与被动引用的区别,以及接口加载的特殊性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本博所有内容都在个人博客橙寂博客中,对jvm解析有错误欢迎大家指出

Java虚拟机的类加载机制

类的加载机制

类的加载过程为

c0231802915504c68eb10073db3e06f1.png


在java中类型的加载,连接与初始化都是在程序运行期间完成

1.加载:
查找并加载类的二进制数据(将类的class文件中的二进制数据读入内存,放在运行时(runtime))
的方法区内。然后创建这个类的class对象(可以反映这个类的数据结构)。这个对象仅是一个class对象并不是实例。
2.连接:
连接可以分为三个步骤

  • 验证:确保被加载类的正确性。验证不通过会报Java.lang.VerifyError。编译验证了,但是并不是所有的class都是由Java源码编译过来,所以进行类加载的时候虚拟机会去验证他。保证系统的安全性。验证分为四项;文件格式验证,元数据验证,字节码验证,符号引用验证
  • 准备:为类的静态对象分配内存,并初始化为默认值(简单来说就是比如int a=1 在这个阶段是不会赋值1的而是赋个默认值)
  • 解析:把类的符合引用转化为实际引用。就是之前是没有指针在虚拟机中都是以CONTENT等类型存在。这个过程会解析成指针的形式

3.初始化
为类的静态变量,赋予正确的的值。(int a=1 为a赋值1)。如果有静态变量或者是static{}语句块编译器会生成一个"()"方法。当被使用了就会去执行这个方法。另外提一句父类的初始化方法优先级都是最高的。

另外还有两个
4.使用: 使用分为两种

所有的java类必须在首次主动使用时才会初始化

  • 主动使用:初始化类
    主动使用场景:
    1.创建类的实例
    2.访问某个类的接口的静态变量。包括静态方法
    3.反射 Class.foName(xxx.xxx.user)
    4.初始化子类 会初始化paren类父类
    5.虚拟机启动时标记为启动类

  • 被动使用:不会初始化类
    除了上面的方式其他的方式都属于被动引用下文中会介绍被动引用的例子
    1.通过子类引用父类的静态变量子类不会初始化

  • 父类源码
    75425b779e939638db71ecdfa5b184b5.png

  • 子类源码
    在这里插入图片描述

  • 测试
    在这里插入图片描述

这里大家先自己先想一下会出现的结果是什么。这个很多企业会出这样一个面试题

这里的话子类调用了父类定义的静态变量子类时不会初始化的,所以这里的输出结果是“ParentClass Loder”另外大家自己再去做个试验把,在子类中定义静态字段,然后在测试类中调用子类的静态变量。一定自己去做一遍印象才会更加深刻。由以上两个例子可以得出以下结论。

  • 结论
    对于静态字段来说,只有直接定义该字段的类才会被初始化
    初始化子类时,要父类全部初始化完了,子类才会初始化

2.通过数组来定义引用类,不会触发此类的初始化
比如 Parent[] arr=new Parent[10];是不会触发初始化的。

3.测试常量
在这里插入图片描述

  • 测试
    在这里插入图片描述

  • 结论
    直接调用常量是不会触发类的初始化的因为常量在项目启动的时候就加载到类的常量池中

注意
接口的加载过程跟类的加载过程是有点不同的,接口没有static{}语句块。但是虚拟机会为接口提供一个"()"类构造器用来初始化静态变量。具体接口为什么不同是针对(上文中的第四点)主动使用的初始化子类,一定要先初始化父类。在接口中初始化子类,并不一定会初始化父类,只有使用到了父接口时才会初始化如(父接口的定义的常量)。

类的加载器(ClassLoader)

在上文中提到的类的加载,所有类的加载都是通过类加载器来实现的而且类的加载器只做了这么一件事

** 1.什么是类的加载器:**
虚拟机团队把类加载阶段中“通过查找类的全限定名来获取二进制流”这个动作放在虚拟机外部实现,来获取应用程序所需要的类。这个代码块叫做类的加载器

对于任何一个类都需要由加载类的加载器和这个类本生确定它在java虚拟中的唯一性换而言之同一个类如果加载它的类加载器不同那么这两个类便不相等。(这里的相等包括Class的Equal方法,isAssignableFrom方法,isInstance方法)

  • 自定义类的加载器
    类加载器是可以自定义的,只是我们一般都默认使用内置的加载器

在这里插入图片描述

  • 系统默认的类加载器(经常使用的)

  • 启动类加载器(BootStrapClassLoader):这个类加载器负责将jdk中rt.jar,或者是以-Xbootclasspath指定的,并且被虚拟机识别了的类库加载到内存中。启动类不可以无法程序调用

  • 扩展类加载器(ExtensionClassLoder):这个加载器负责加载lib下ext下的扩展包或者是以其他方式指定的,这个加载器开发者可以调用

  • 应用程序类加载器(ApplicationClassLoader):这个类加载器是系统中geSystemClassLoader的返回值,所以这个加载器也就是我们所说的系统加载器也是我们开发者可能最常用的。

  • 双亲委派模型
    在我们的程序中都是这几种加载器一起配合加载的,所以这种模型也称双亲委派模型(除了顶层外都有父级)这个模型的流程是:每个类收到加载需求的时候,都是把这个请求委派给父级而不是自己就尝试加载。如果父级加载不了,子级才会加载(不是强制性的只是java设计者推荐给大家的开发模型)。
    在这里插入图片描述

  • 代码实现双亲委派模型
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值