关于java:J2SE-II一一类加载机制及反射详解

4次阅读

共计 4261 个字符,预计需要花费 11 分钟才能阅读完成。

一、Java 类加载机制

1.概述

 Class 文件由类装载器装载后,在 JVM 中将造成一份形容 Class 构造的元信息对象,通过该元信息对象能够获知 Class 的构造信息:如构造函数,属性和办法等,Java 容许用户借由这个 Class 相干的元信息对象间接调用 Class 对象的性能。

虚拟机把形容类的数据从 class 文件加载到内存,并对数据进行校验,转换解析和初始化,最终造成能够被虚拟机间接应用的 Java 类型,这就是虚拟机的类加载机制。

2.工作机制

类装载器就是寻找类的字节码文件,并结构出类在 JVM 外部示意的对象组件。在 Java 中,类装载器把一个类装入 JVM 中,要通过以下步骤:
(1) 装载:查找和导入 Class 文件;
(2) 链接:把类的二进制数据合并到 JRE 中;
(a)校验:查看载入 Class 文件数据的正确性;
(b)筹备:给类的动态变量调配存储空间;
(c)解析:将符号援用转成间接援用;
(3) 初始化:对类的动态变量,动态代码块执行初始化操作

Java 程序能够动静扩大是由运行期动静加载和动静链接实现的;比方:如果编写一个应用接口的应用程序,能够等到运行时再指定其理论的实现(多态),解析过程有时候还能够在初始化之后执行;比方:动静绑定(多态);

【类初始化】

(1) 遇到 new、getstatic、putstatic 或 invokestatic 这 4 条字节码指令时,如果类没有进行过初始化,则须要先触发其初始化。生成这 4 条指令的最常见的 Java 代码场景是:应用 new 关键字实例化对象的时候,读取或设置一个类的动态字段(被 final 润饰、已在编译期把后果放入常量池的动态字段除外)的时候,以及调用一个类的静态方法的时候。

(2) 应用 java.lang.reflect 包的办法对类进行反射调用的时候,如果类没有进行过初始化,则须要先触发其初始化。

(3) 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则须要先触发其父类的初始化。

(4)当虚拟机启动时,用户须要指定一个要执行的主类(蕴含 main()办法的那个类),虚构机会先初始化这个主类。

只有上述四种状况会触发初始化,也称为对一个类进行被动援用,除此以外,所有其余形式都不会触发初始化,称为被动援用

代码清单 1

上述代码运行后,只会输入【—SuperClass init】, 而不会输入【SubClass init】, 对于动态字段,只有间接定义这个字段的类才会被初始化, 因而,通过子类来调用父类的动态字段,只会触发父类的初始化, 然而这是要看不同的虚拟机的不同实现。

代码清单 2

此处不会引起 SuperClass 的初始化,然而却触发了【[Ltest.SuperClass】的初始化,通过 arr.toString()能够看出,对于用户代码来说,这不是一个非法的类名称,它是由虚拟机主动生成的,间接继承于 Object 的子类,创立动作由字节码指令 newarray 触发, 此时数组越界查看也会随同数组对象的所有调用过程,越界查看并不是封装在数组元素拜访的类中,而是封装在数组拜访的 xaload,xastore 字节码指令中.

代码清单 3

对常量 ConstClass.value 的援用理论都被转化为 NotInitialization 类对本身常量池的援用,这两个类被编译成 class 后不存在任何分割。
【装载】

 在装载阶段,虚拟机须要实现以下 3 件事件

 (1) 通过一个类的全限定名来获取定义此类的二进制字节流

 (2) 将这个字节流所代表的动态存储构造转化为办法区的运行时数据结构

 (3) 在 Java 堆中生成一个代表这个类的 java.lang.Class 对象,作为办法区这些数据的拜访入口。

 虚拟机标准中并没有精确阐明二进制字节流应该从哪里获取以及怎么获取, 这里能够通过定义本人的类加载器去管制字节流的获取形式。

【验证】

 虚拟机如果不查看输出的字节流,对其齐全信赖的话,很可能会因为载入了无害的字节流而导致系统奔溃。

【筹备】

筹备阶段是正式为类变量调配并设置类变量初始值的阶段,这些内存都将在办法区中进行调配, 须要阐明的是:

这时候进行内存调配的仅包含类变量(被 static 润饰的变量), 而不包含实例变量, 实例变量将会在对象实例化时随着对象一起调配在 Java 堆中; 这里所说的初始值“通常状况”是数据类型的零值,如果:

public static int value = 123;

value 在筹备阶段过后的初始值为 0 而不是 123, 而把 value 赋值的 putstatic 指令将在初始化阶段才会被执行

二、类加载器与双亲委派模型

  类加载器

(1) Bootstrap ClassLoader : 将寄存于 <JAVA_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的门路中的,并且是虚拟机辨认的(仅依照文件名辨认,如 rt.jar 名字不合乎的类库即便放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无奈被 Java 程序间接援用

(2) Extension ClassLoader : 将 <JAVA_HOME>\lib\ext 目录下的,或者被 java.ext.dirs 零碎变量所指定的门路中的所有类库加载。开发者能够间接应用扩大类加载器。

(3) Application ClassLoader : 负责加载用户类门路 (ClassPath) 上所指定的类库, 开发者可间接应用。

双亲委派模型

工作过程:如果一个类加载器接管到了类加载的申请,它首先把这个申请委托给他的父类加载器去实现,每个档次的类加载器都是如此,因而所有的加载申请都应该传送到顶层的启动类加载器中,只有当父加载器反馈本人无奈实现这个加载申请(它在搜寻范畴中没有找到所需的类)时,子加载器才会尝试本人去加载。

 益处:java 类随着它的类加载器一起具备了一种带有优先级的档次关系。例如类 java.lang.Object,它寄存在 rt.jar 中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因而 Object 类在程序的各种类加载器环境中都是同一个类。相同,如果用户本人写了一个名为 java.lang.Object 的类,并放在程序的 Classpath 中,那零碎中将会呈现多个不同的 Object 类,java 类型体系中最根底的行为也无奈保障,应用程序也会变得一片凌乱。

 java.lang.ClassLoader 中几个最重要的办法:

// 加载指定名称(包含包名)的二进制类型,供用户调用的接口
public Class<?> loadClass(String name);
// 加载指定名称(包含包名)的二进制类型,同时指定是否解析(然而,这里的 resolve 参数不肯定真正能达到解析的成果),供继承用
protected synchronized Class<?> loadClass(String name, boolean resolve);
protected Class<?> findClass(String name)
// 定义类型,个别在 findClass 办法中读取到对应字节码后调用,能够看出不可继承(阐明:JVM 曾经实现了对应的具体性能,解析对应的字节码,产生对应的外部数据结构搁置到办法区,所以无需覆写,间接调用就能够了)protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError{}

如下是实现双亲委派模型的次要代码:

三、反射

 Reflection 机制容许程序在正在执行的过程中,利用 Reflection APIs 获得任何已知名称的类的外部信息,包含:package、type parameters、superclass、implemented interfaces、inner classes、outer classes、fields、constructors、methods、modifiers 等,并能够在执行的过程中,动静生成 instances、变更 fields 内容或唤起 methods。

 1、获取构造方法

Class 类提供了四个 public 办法,用于获取某个类的构造方法。

Constructor getConstructor(Class[] params)     

依据构造函数的参数,返回一个具体的具备 public 属性的构造函数

Constructor getConstructors()     

返回所有具备 public 属性的构造函数数组

Constructor getDeclaredConstructor(Class[] params)     

依据构造函数的参数,返回一个具体的构造函数(不分 public 和非 public 属性)

Constructor getDeclaredConstructors()    

返回该类中所有的构造函数数组(不分 public 和非 public 属性)

2、获取类的成员办法

与获取构造方法的形式雷同,存在四种获取成员办法的形式。

Method getMethod(String name, Class[] params)    

依据办法名和参数,返回一个具体的具备 public 属性的办法

Method[] getMethods()    

返回所有具备 public 属性的办法数组

Method getDeclaredMethod(String name, Class[] params)    

依据办法名和参数,返回一个具体的办法(不分 public 和非 public 属性)

Method[] getDeclaredMethods()    

返回该类中的所有的办法数组(不分 public 和非 public 属性)

3、获取类的成员变量(成员属性)

存在四种获取成员属性的办法

Field getField(String name)    

依据变量名,返回一个具体的具备 public 属性的成员变量

Field[] getFields()  

返回具备 public 属性的成员变量的数组

Field getDeclaredField(String name)  

依据变量名,返回一个成员变量(不分 public 和非 public 属性)

Field[] getDelcaredFields()    

返回所有成员变量组成的数组(不分 public 和非 public 属性)

正文完
 0