【Java虚拟机——类加载】

类加载是Java运行时的重要环节,包括加载、链接(验证、准备、解析)和初始化。加载阶段将字节码文件转为内存中的数据结构,链接阶段校验字节码并分配静态变量空间,初始化则为静态变量赋予正确初始值。类加载器包括引导类加载器、扩展类加载器和应用程序类加载器,遵循双亲委派机制,防止类的重复加载和确保安全。打破双亲委派可通过重写ClassLoader的findClass()方法。
摘要由CSDN通过智能技术生成

类加载

作用

将字节码文件加载到内存中(运行时数据区中的方法区内存)

类加载过程分为三步

1.加载(将磁盘/网络中的字节码文件存放到内存中,类似于形态的转换将字节码文件转换为对象的形式存在于内存中供Java虚拟机可以访问到)

a.通过类的全限定(包名 + 类名)名获取类的字节流(二进制字节流:字节码文件是二进制文件)

b.将字节流中的静态存储结构进行转换,转换为方法区内存中数据的存储结构(方法区内存中数据的存储结构完全由虚拟机实现自行定义,《Java虚拟机规范》中没有规定方法区内存中的数据结构是什么样子的)

c.在堆中创建一个该类的Class对象,只有通过该类的Class对象才能访问该类的属性和方法

2.链接

验证

a.验证字节码文件的格式是否正确(字节码文件的开头是否有特定的标识等)

b.验证是否存在语法错误

准备

为类中的静态变量进行内存分配并赋初始值,静态常量在编译时就已经进行了初始化


public static int age = 12;// 这里age会先被赋值为int类型的默认初始值0

解析

将类的二进制数据中的符号引用替换成直接引用

3.初始化

为类的静态变量赋正确的初始值,类的初始化顺序是从上到下依次进行


public staticint age = 12;// 这时age才会被赋值为12

例如:


public class Test {

    static {
        age = 20;
    }

    public static int age = 10;

    public static void main(String[] args) {
        System.out.println(Test.age);// 结果为10
    }

}

输出的结果为10


public class Test {

    public static int age = 10;

    static {
        age = 20;
    }
    
    public static void main(String[] args) {
        System.out.println(Test.age);// 结果为20
    }

}

输出的结果就是20

这是因为类的静态变量与静态代码块是同一级的,所以在类的初始化过程中会按从上到下的顺序进行加载

可能有人会问:为什么第一段代码明明age变量定义在静态代码块之后却不会报错?

因为在类的准备阶段就已经为类的静态变量开辟的空间,也就意味着在类的准备阶段变量age已经存在,所以说才不会报错。

类会进行加载的情况(类初始化完成)

  1. 在类中运行main方法

  1. 调用类中的静态变量和静态方法

  1. 子类被加载,父类也会被加载

  1. 使用Class.forName()方法进行类的反射

  1. 创建该类的对象

类不会进行加载的情况(类还没有进行到初始化完成这一步)

  1. 调用类中的静态常量,因为类中的静态常量在Java文件编译阶段已经进行了初始化所以不需要对该类进行加载

  1. 创建该类类型的数组,这里该类只是被当作类型参数而存在,所以不会被加载

类加载器

  1. 引导类加载器

最顶层的类加载器,与Java无关,底层使用C/C++实现,加载Java的核心类库,用于启动Java虚拟机,特别的String类是由引导类加载器进行加载

  1. 扩展类加载器

继承于ClassLoader类,加载JDK目录下jre/lib/ext目录下的类

  1. 应用程序类加载器

继承于ClassLoader类,加载用户类路径环境变量(ClassPath)中所有的类库,也可以加载用户自己编写的类

所有的类加载器都需要继承ClassLoader类除了引导类加载器

双亲委派机制

当一个类加载器接收到类加载的请求时不会直接去加载这个类,而是会先将加载类的任务委派给其类加载器的父类,每一个类加载器都是这样一直到引导类加载器,然后从引导类加载器开始进行尝试加载该类,如果加载成功就直接返回请求完成,如果加载失败就将请求传给下一级,如果所有的类加载器都没有加载成功,就会产生ClassNotFoundException异常。

双亲委派机制的优点

1.用户自己编写的类不会对Java原有的类进行覆盖

2.避免类重复加载,在双亲委派机制中loadClass()方法首先会使用findLoadedClass()方法判断请求被加载的类是否已经被加载过了,如果已经被加载就直接返回该类的对象,如果没有被加载则返回null,所以避免了类的重复加载。(双亲委派机制通过直接返回类的对象来避免类的重复加载)

如果一个类被多个类加载器加载了会发生什么情况?

········

打破双亲委派机制

我们可以通过继承ClassLoader类创建自定义的类加载器,除了引导类加载器以外,所有的类加载器都是ClassLoader类的派生类,ClassLoader类中有两个方法没有使用final关键字修饰,表示方法可以被重写,所以我们可以通过重写这两个方法来实现打破双亲委派机制。

loadClass()

由于loadClass()方法中就是实现双亲委派机制逻辑的地方,重写方法会打破双亲委派机制,所以不推荐使用该方法。

findClass()

findClass()方法中描述的是类被加载的过程,所以我们也可以通过重写findClass()方法来打破双亲委派机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值