java 类加载机制和反射详解及实例代码

2025-05-29 0 43

一、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 类加载机制和反射详解及实例代码

java程序可以动态扩展是由运行期动态加载和动态链接实现的;比如:如果编写一个使用接口的应用程序,可以等到运行时再指定其实际的实现(多态),解析过程有时候还可以在初始化之后执行;比如:动态绑定(多态);

初始化】

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

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

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

(4)当虚拟机启动时,用户需要指定一个要执行的主(包含main()方法的那个),虚拟机会先初始化这个主

只有上述四种情况会触发初始化,也称为对一个进行主动引用,除此以外,所有其他方式都不会触发初始化,称为被动引用

代码清单1

java 类加载机制和反射详解及实例代码

上述代码运行后,只会输出【—superclassinit】,而不会输出【subclassinit】,对于静态字段,只有直接定义这个字段的才会被初始化,因此,通过子来调用父的静态字段,只会触发父的初始化,但是这是要看不同的虚拟机的不同实现。

代码清单2

java 类加载机制和反射详解及实例代码

此处不会引起superclass的初始化,但是却触发了【[ltest.superclass】的初始化,通过arr.tostring()可以看出,对于用户代码来说,这不是一个合法的名称,它是由虚拟机自动生成的,直接继承于object的子,创建动作由字节码指令newarray触发,此时数组越界检查也会伴随数组对象的所有调用过程,越界检查并不是封装在数组元素访问的中,而是封装在数组访问的xaload,xastore字节码指令中.

代码清单3

java 类加载机制和反射详解及实例代码

对常量constclass.value的引用实际都被转化为notinitialization对自身常量池的引用,这两个被编译成class后不存在任何联系。

【装载】

在装载阶段,虚拟机需要完成以下3件事情

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

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

(3)在java堆中生成一个代表这个的java.lang.class对象,作为方法区这些数据的访问入口。

虚拟机规范中并没有准确说明二进制字节流应该从哪里获取以及怎样获取,这里可以通过定义自己的加载器去控制字节流的获取方式。

【验证】

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

【准备】

准备阶段是正式为变量分配并设置变量初始值的阶段,这些内存都将在方法区中进行分配,需要说明的是:

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

?

1
public static int value = 123;

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

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

加载器

(1)bootstrapclassloader:将存放于<java_home>\\lib目录中的,或者被-xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar名字不符合的库即使放在lib目录中也不会被加载)库加载到虚拟机内存中。启动加载器无法被java程序直接引用

(2)extensionclassloader:将<java_home>\\lib\\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有库加载。开发者可以直接使用扩展加载器。

(3)applicationclassloader:负责加载用户路径(classpath)上所指定的库,开发者可直接使用。

双亲委派模型

java 类加载机制和反射详解及实例代码

工作过程:如果一个加载器接收到了加载的请求,它首先把这个请求委托给他的父加载器去完成,每个层次的加载器都是如此,因此所有的加载请求都应该传送到顶层的启动加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的)时,子加载器才会尝试自己去加载。

好处:java随着它的加载器一起具备了一种带有优先级的层次关系。例如java.lang.object,它存放在rt.jar中,无论哪个加载器要加载这个,最终都会委派给启动加载器进行加载,因此object在程序的各种加载器环境中都是同一个。相反,如果用户自己写了一个名为java.lang.object的,并放在程序的classpath中,那系统中将会出现多个不同的object,java型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。

java.lang.classloader中几个最重要的方法:

?

1

2

3

4

5

6

7
//加载指定名称(包括包名)的二进制类型,供用户调用的接口

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{}

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

java 类加载机制和反射详解及实例代码

三、反射

reflection机制允许程序在正在执行的过程中,利用reflectionapis取得任何已知名称的的内部信息,包括:package、typeparameters、superclass、implementedinterfaces、innerclasses、outerclasses、fields、constructors、methods、modifiers等,并可以在执行的过程中,动态生成instances、变更fields内容或唤起methods。

1、获取构造方法

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

constructorgetconstructor(class[]params)

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

    constructorgetconstructors()

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

    constructorgetdeclaredconstructor(class[]params)

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

    constructorgetdeclaredconstructors()

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

java 类加载机制和反射详解及实例代码

2、获取的成员方法

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

methodgetmethod(stringname,class[]params)

根据方法名和参数,返回一个具体的具有public属性的方法

    method[]getmethods()

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

    methodgetdeclaredmethod(stringname,class[]params)

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

    method[]getdeclaredmethods()

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

java 类加载机制和反射详解及实例代码

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

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

    fieldgetfield(stringname)

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

    field[]getfields()

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

    fieldgetdeclaredfield(stringname)

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

    field[]getdelcaredfields()

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

参考:

《深入理解jvm虚拟机》

感谢阅读 ,希望能帮助到大家,谢谢大家对本站的支持!

原文链接:http://blog.csdn.net/qq_35101189/article/details/60574653

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 java 类加载机制和反射详解及实例代码 https://www.kuaiidc.com/118476.html

相关文章

发表评论
暂无评论