forName、loadClass、NewInstance、new

一,类加载的过程

forName、loadClass、NewInstance、new_第1张图片


1,加载

通过一个类的全限定名来获取此类的二进制字节流,将这个字节流所代表的静态存储结构转换成方法区的运行时数据结构,在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

注意:Class对象不一定放在堆里,对于Hospot虚拟机,放在了方法区里

2,验证

验证Class文件的文件流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机安全

①文件格式验证

保证输入的字节流能正确地解析并存储于方法区内,格式符合要求。此阶段验证基于二进制字节流进行,此阶段验证完毕后,字节流才会进入内存的方法区,后面的验证阶段都是基于内存方法区的存储结构进行的,而不是基于字节流。

②元数据验证

对字节码描述的信息进行语义分析,确保符合Java语言规范

如验证此类是否有父类.除了Object都要有父类

验证此类的父类是否继承了不允许被继承的类(如final)

如果此类不是抽象类,是否实现了其父类或接口要求实现的方法

类中的字段方法是否与父类相矛盾,如覆盖父类final方法,重载时返回值类型不同

。。。

③字节码验证

确保程序语义合法。检查类的方法体。

保证方法体的类型转换是有效的,如可以把一个子类对象赋值给父类数据类型,但是不可以反过来。

④符号引用验证

发生在连接的第三个阶段——解析时,即虚拟机将符号引用转换成直接引用的时候。

3,准备

为类的静态变量设置内存并设置为虚拟机默认初始值,一般为零值,如static int a=100; a值在准备阶段为0。赋值为100的指令是程序被编译后,存放于类构造器方法之中,在初始化阶段才会执行。

但是对于final修饰的常量,在准备阶段就会直接赋值。如final static b=100.在准备阶段将b赋值为100

4,解析

虚拟机将常量池内的符号引用替换成直接引用

5,初始化

以下四种属于对类进行主动引用,会触发初始化

①遇到new、putstatic、setstatic、invokestatic这四条字节码指令时,如果没有初始化类,就要先初始化。对应java代码场景是:new 关键字实例化对象;设读取或设置类的静态字段;调用类的静态方法

②通过反射执行上述情况

③初始化子类时,如果父类没有初始化,先初始化父类

④作为主类调用main方法,会初始化主类


对类进行被动引用不会触发初始化

引用父类的静态字段,只会引起父类的初始化,而不会引起子类的初始化。
定义类数组,不会引起类的初始化。
引用类的常量,不会引起类的初始化。


二,Class.forName(className) 实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须被初始化。
ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false),第二个参数指出Class是否被link。
区别就出来了。Class.forName(className)装载的class已经被初始化,而ClassLoader.loadClass(className)装载的class还没有被link。
forName支持数组类型,loadClass不支持数组
一般情况下,这两个方法效果一样,都能装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。

只有执行cls.NewInstance()才能够得到该类的一个实例。NewInstance在得到实例前会先初始化。


你可能感兴趣的:(java,反射,类加载)