一、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属性)
相关推荐
Java的类加载机制:加载,连接,初始化。JAVA类加载器: Bootstrap ClassLoader : 根类加载器, Extension ClassLoader: 扩展类加载器, System ClassLoader : 系统类加载器, Java反射
反射机制和类加载机制学习总结 Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并...
java 注解 反射 字节码 类加载 包括pdf资料以及全部源码文件,具有非常大参考价值和学习意义
主要介绍了java 类加载机制和反射详解及实例代码的相关资料,需要的朋友可以参考下
本学习讲义是关于java类加载和反射机制需要注意的要点学习,内容详细
Java 程序在运行期间可以动态加载、解析和使用一些在编译阶段并不确定的类型数据,这一机制被称为反射(Reflection)。反射库(reflection library)提供了一个非常丰富且精心设计的工具类,以便编写能够动态操纵Java...
JAVA反射机制与类的加载,详细的说明java反射机制的实例以及应用是入门级的课件
反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。 框架=反射+注解+设计模式 1.是什么? 加载完类之后,在堆内的方法区中就产生了一个Class类型的...
(类的加载概述和加载时机) (类加载器的概述和分类) (获取class文件对象的三种方式) (通过反射获取无参构造方法并使用) (通过反射获取带参构造方法并使用) (通过反射获取私有构造方法并使用) (通过反射获取成员变量并...
IO(输入/输出)是所有程序都必需的部分:使用输入机制,允许程序读取外部数据(包括来自磁盘、光盘等存储设备的数据)、用户输入数据;使用输出机制,允许程序记录运行状态,将程序数据输出到磁盘、光盘等存储设备...
/*主要说明如何通过反射实现动态加载DLL, * 因为.Net底层有自动回收机制,所以不需要考虑这个问题 * ADD by Brian 2008/01/21 参考MSDN */
Java的反射机制是指在运行时通过分析类的信息实现动态调用类的方法和访问类的属性的能力。它允许程序在运行时获取类的信息并操作类或对象的属性、方法和构造函数等。通过反射机制,可以实现很多高级特性,如动态代理...
理解 Class 类 理解 Java 的类加载机制 学会使用 ClassLoader 进行类加载 理解反射的机制 掌握 Constructor、Method、Field 类的用法 理解并掌握动态代理
Android动态加载与反射机制的静态污点分析研究.pdf
里面主要是介绍Java反射的只是,包括介绍Class类的初始化,如何通过反射动态加载类和获取类的信息等。
1.不使用反射机制就可以动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型 2.应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到该技术。 3.主要应用与...
反射是指程序在运行时动态获取对象属性与方法的一种机制,即编译器需要将类型信息(属性类型与偏移地址以及成员函数的地址等信息)编译到程序文件中,当程序运行时将这些信息加载到内存中去,做到运行时只根据对象的...
spring基于xml配置加载bean 的demo 了解spring的反射机制
3.1 应用场景 在一些开源框架里,如Spring,Struts,Hibernate,MyBatis等,应用程序会提供一个配置文件如xml文件或者properties,然后在Java类里面解析xml或properties里面的内容,得到一个字符串,然后用反射机制,...