0 使用类的准备工作
任何程序都需要加载到内存才能与CPU进行交流,同理, 字节码.class文件同样需要加载到内存中,才可以实例化类。
ClassLoader
的使命就是提前加载.class 类文件到内存中,在加载类时,使用的是Parents Delegation Model(溯源委派加载模型)。
Java的类加载器是一个运行时核心基础设施模块,主要是在启动之初进行类的加载、链接、初始化:
- Java 类加载过程
Load-加载
由类加载器执行。
读取类文件(通常在 classpath 所指定的路径中查找,但classpath非必须),查找字节码,从而产生二进制流,并转为特定数据结构,初步校验cafe babe魔法数、常量池、文件长度、是否有父类等,然后创建对应类的java.lang.Class实例。
Link-链接
将已读入内存的类的二进制数据合并到 JVM 运行时环境。
包括验证、准备、解析三步:
验证
确保被加载类的正确性。验证类中的字节码,是更详细的校验,比如final是否合规、类型是否正确、静态变量是否合理
准备
为类的static字段分配内存,并设定初始默认值,解析类和方法确保类与类之间的相互引用正确性,完成内存结构布局
解析
如果需要的话,将解析这个类创建的对其他类的所有引用,将常量池的符号引用转换成直接引用 。
Init-初始化
执行类构造器 方法,如果赋值运算是通过其他类的静态方法来完成的,那么会马上解析另外一个类,在虚拟机栈中执行完毕后通过返回值进行赋值
类加载是一个将.class字节码文件实例化成Class对象并进行相关初始化的过程。
在这个过程中,JVM会初始化继承树上还没有被初始化过的所有父类,并且会执行这个链路上所有未执行过的静态代码块、静态变量赋值语句等。
某些类在使用时,也可以按需由类加载器进行加载。
全小写的class是关键字,用来定义类
而首字母大写的Class,它是所有class的类
这句话理解起来有难度,类已经是现实世界中某种事物的抽象,为什么这个抽象还是另外一个类Class的对象?
示例代码如下:
第1处说明:
Class类下的newInstance()在JDK9中已经置为过时,使用getDeclaredConstructor().newInstance()的方式
着重说明一下new与newInstance的区别
new是强类型校验,可以调用任何构造方法,在使用new操作的时候,这个类可以没有被加载过
而Class类下的newInstance是弱类型,只能调用无参构造方法
如果没有默认构造方法,就拋出InstantiationException异常;
如果此构造方法没有权限访问,则拋 IllegalAccessException异常
Java 通过类加载器把类的实现与类的定义进行解耦,所以是实现面向接口编程、依赖倒置的必然选择。
● 第2处说明:
可以使用类似的方式获取其他声明,如注解、方法等
第3处说明: private 成员在类外是否可以修改?
通过setccessible(true)
,即可使用Class类的set方法修改其值
如果没有这一步,则抛出如下异常:
参考