类的加载、链接、初始化

2022-08-29 09:08:13

所有的类都是在对其第一次使用时,动态地加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。(构造器也是类的静态方法,即使在构造器之前并没有使用static关键字,因此使用new操作符创建类的新对象也会被当做对类的静态成员的引用)

获取Class对象

无论何时,如果想在运行时使用类型信息,就必须首先获得恰当的Class对象引用:
(1)Class.forName()是取得Class对象的引用的一种方法(传入包名加类3名),对forName()的调用产生了“副作用”,如果这个类还没有加载就加载它。在加载的过程中 类的static语句被执行
(2)如果已经用了感兴趣的类型的Class对象,那就可以通过调用getClass()方法来获取Class引用了,这个方法属于根类Object的一部分,它将返回该对象的实际类型的Class引用。
(3)Java还提供了字类面常量来生成对Class对象(FancyToy.class)。这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(不需要放入try语句块中)根除了对forName()方法的调用,所以更高效。类字面常量不仅可以应用于普通的类,也可以应用于接口,数组以及基本数据类型。基本数据类型还可以 Boolean.Type 等价于 Boolean.class、boolean.class。

Class的newInstance()来创建对象,必须带有默认无参构造器
.class 语法来获取对类的引用不会引发初始化,但是Class.forName()立即进行了初始化

类的加载

(1)加载(Loading),由类加载器执行,a查找字节码b并创建该类的Class对象
(2)链接(Linking)a验证类中的字节码,b(准备) 为类的静态属性分配存储空间,并设置默认值,c(解析)将类的二进制数据中的符号引用替换为直接引用
(3)初始化(Initialization)(尽可能的惰性)。a.假如这个类还没有被加载和链接,程序先执行加载并链接这个类。b假如该类的直接父类还没有被初始化,先初始化其直接父类(这个阶段当前类已经加载和链接了)。c假如类中有初始化语句,则程序
依次执行这些初始化语句

初始化被延迟到了对静态方法(构造器隐式是静态的)或者非常数静态域进行首次引用时才执行。注意:编译器常量必须是static final域且在定义时初始化而不能是在静态块中初始化(下面两句话便是对这句话的解释)
(1)如果一个 static final 值是“编译期常量(因为static final也可以在静态块中初始化)”,不需要对类进行初始化就可以被读取。(方法调用、静态块中初始化的常量和定义时初始化但不是常量的除外)
(2)如果一个static域不是final的,那么访问前,必须先进行链接(为这个域分配存储空间)和初始化(初始化该存储空间)
(3)说白了除了 static final 编译期常量调用时和.class时不会初始化,其他时候都要先初始化。

  • 作者:CmdSmith
  • 原文链接:https://blog.csdn.net/CmdSmith/article/details/88538349
    更新时间:2022-08-29 09:08:13