1.概述java
Class文件由类装载器装载后,在JVM中将造成一份描述Class结构的元信息对象,经过该元信息对象能够获知Class的结构信息:如构造函数,属性和方法等,Java容许用户借由这个Class相关的元信息对象间接调用Class对象的功能。数组
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终造成能够被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。数据结构
2.工做机制函数
类装载器就是寻找类的字节码文件,并构造出类在JVM内部表示的对象组件。在Java中,类装载器把一个类装入JVM中,要通过如下步骤:spa
(1) 装载:查找和导入Class文件;3d
(2) 连接:把类的二进制数据合并到JRE中;code
(a)校验:检查载入Class文件数据的正确性;orm
(b)准备:给类的静态变量分配存储空间;对象
(c)解析:将符号引用转成直接引用;blog
(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。
一、获取构造方法
Class类提供了四个public方法,用于获取某个类的构造方法。
Constructor getConstructor(Class[] params)
根据构造函数的参数,返回一个具体的具备public属性的构造函数
Constructor getConstructors()
返回全部具备public属性的构造函数数组
Constructor getDeclaredConstructor(Class[] params)
根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors()
返回该类中全部的构造函数数组(不分public和非public属性)
二、获取类的成员方法
与获取构造方法的方式相同,存在四种获取成员方法的方式。
Method getMethod(String name, Class[] params)
根据方法名和参数,返回一个具体的具备public属性的方法
Method[] getMethods()
返回全部具备public属性的方法数组
Method getDeclaredMethod(String name, Class[] params)
根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods()
返回该类中的全部的方法数组(不分public和非public属性)
三、获取类的成员变量(成员属性)
存在四种获取成员属性的方法
Field getField(String name)
根据变量名,返回一个具体的具备public属性的成员变量
Field[] getFields()
返回具备public属性的成员变量的数组
Field getDeclaredField(String name)
根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredFields()
返回全部成员变量组成的数组(不分public和非public属性)