java reflect

一.什么是反射

反射,一种计算机处理方式。是程序可以访问、检测和修改它本身状态或行为的一种能力。java反射使得我们可以在程序运行时动态加载一个类,动态获取类的基本信息和定义的方法,构造函数,域等。除了检阅类信息外,还可以动态创建类的实例,执行类实例的方法,获取类实例的域值。反射使java这种静态语言有了动态的特性

二.反射机制的作用

    • 在运行时判断任意一个对象所属的类;

    • 在运行时构造任意一个类的对象;

    • 在运行时判断任意一个类所具有的成员变量和方法;

    • 在运行时调用任意一个对象的方法;

    • 生成动态代理。

 三.反射机制的优点与缺点

静态编译:在编译时确定类型,绑定对象,即通过。

动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

反射机制的优点:可以实现动态创建对象和编译,体现出很大的灵活性。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。

反射机制的缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。

四.利用反射机制能获得什么信息

获取类3中方式


获得构造函数的方法

Constructor getConstructor(Class[] params)//根据指定参数获得public构造器

Constructor[] getConstructors()//获得public的所有构造器

Constructor getDeclaredConstructor(Class[] params)//根据指定参数获得public和非public的构造器

Constructor[] getDeclaredConstructors()//获得public的所有构造器

获得类方法的方法

Method getMethod(String name, Class[] params),根据方法名,参数类型获得方法

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[] getDeclaredFields()//获得类中所有的public和非public方法


运行时调用methods

首先准备一个Class[]做为参数类型,然后调用getMethod(),获得特定的Method object。接下来准备一个Object[]放置变量,然后调用上述所得之特定Method object的invoke()。

调用私有方法,需要抑制访问控制检查

method.setAccessible(true)会赋值给override

利用反射实现工厂模式

根据完整类名返回类的对象

传入类名

配置文件结合反射实现工厂模式(模拟IOC)

读取properties文件内容

动态代理

获取类的构造函数(代码)

在获取一个类的class后,我们可以通过反射获取一个类的所有构造函数,class类内部封装了如下方法用于提取构造函数:

上面的代码片段主要就是读取配置,检查缓存中是否有有效数据,如果有,直接从缓存中返回,如果没有,调用native的方法从jvm中请求数据,然后设置到缓存。比较重要的是reflectionData()这个调用。这个方法主要是用于延迟创建并缓存ReflectionData对象,注意是对象,里面并没有保存反射数据,这些数据只有在第一次执行相应的反射操作后才会被填充。下面是这个方法的实现代码:

SoftReference声明reflectionData是一个软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存,不过也可以通-XX:SoftRefLRUPolicyMSPerMB参数控制回收的时机

下面看一下newReflectionData()的实现:

上面的几个代码片段是反射获取一个类的构造函数的主要方法调用。主要流程就是先从class内部的reflectionData缓存中读取数据,如果没有缓存数据,那么就从jvm中去请求数据,然后设置到缓存中,供下次使用。

ReflectionData

为了提高反射的性能,缓存显然是必须的。class类内部有一个useCaches静态变量来标记是否使用缓存,这个值可以通过外部配置项sun.reflect.noCaches进行开关。class类内部提供了一个ReflectionData内部类用来存放反射数据的缓存

执行构造函数

在通过反射获取到一个类的构造函数我们,一般我们会试图通过构造函数去实例化声明这个构造函数的类,如下:

MyClass myClass = (MyClass) constructor.newInstance();

下面来看看这个newInstance()到底发生了什么:

上面代码主要就是做权限检查,如果权限通过,则通过执行acquireConstructorAccessor()获取一个ConstructorAccessor来真正执行newInstance,真正工作的是ReflectionFactory.newConstructorAccessor(),这个方法的主要工作就是为一个Constructor动态生成针对ConstructorAccessor接口的实现ConstructorAccessorImpl,下面来看一下这个方法实现:

由上面代码可见,在生成ConstructorAccessorImpl的操作上,一共提供多种版本的实现:直接抛出异常的实现,native执行的实现,asm生成代码的实现。native的实现比较简单,asm 版本的主要就是字节码操作了

Method获取

调用Class类的getDeclaredMethod可以获取指定方法名和参数的方法对象Method。

getDeclaredMethod

其中privateGetDeclaredMethods方法从缓存或JVM中获取该Class中申明的方法列表,searchMethods方法将从返回的方法列表里找到一个匹配名称和参数的方法对象。

searchMethods

如果找到一个匹配的Method,则重新copy一份返回,即Method.copy()方法

所次每次调用getDeclaredMethod方法返回的Method对象其实都是一个新的对象,且新对象的root属性都指向原来的Method对象,如果需要频繁调用,最好把Method对象缓存起来。

privateGetDeclaredMethods

从缓存或JVM中获取该Class中申明的方法列表,实现如下:


Method调用

获取到指定的方法对象Method之后,就可以调用它的invoke方法了,invoke实现如下:

应该注意到:这里的MethodAccessor对象是invoke方法实现的关键,一开始methodAccessor为空,需要调用acquireMethodAccessor生成一个新的MethodAccessor对象,MethodAccessor本身就是一个接口,实现如下:

在acquireMethodAccessor方法中,会通过ReflectionFactory类的newMethodAccessor创建一个实现了MethodAccessor接口的对象,实现如下:

在ReflectionFactory类中,有2个重要的字段:noInflation(默认false)和inflationThreshold(默认15),在checkInitted方法中可以通过-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true对这两个字段重新设置,而且只会设置一次;

如果noInflation为false,方法newMethodAccessor都会返回DelegatingMethodAccessorImpl对象,


DelegatingMethodAccessorImpl的类实现

其实,DelegatingMethodAccessorImpl对象就是一个代理对象,负责调用被代理对象delegate的invoke方法,其中delegate参数目前是NativeMethodAccessorImpl对象,所以最终Method的invoke方法调用的是NativeMethodAccessorImpl对象invoke方法,实现如下:

这里用到了ReflectionFactory类中的inflationThreshold,当delegate调用了15次invoke方法之后,如果继续调用就通过MethodAccessorGenerator类的generateMethod方法生成MethodAccessorImpl对象,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。

这里需要注意的是:

generateMethod方法在生成MethodAccessorImpl对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass创建对应的class对象,实现如下:

在ClassDefiner.defineClass方法实现中,每被调用一次都会生成一个DelegatingClassLoader类加载器对象

这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行了。

Beantuils

Beanutils工具在使用时几乎只用到以下几个方法,其中一个方法通常情况下都是使用匿名内部类。

BeanUtils.setProperty(bean, name, value);其中bean是指你将要设置的对象,name指的是将要设置的属性(写成”属性名”),value(从配置文件中读取到到的字符串值)


BeanUtils.copyProperties(bean, name, value),和上面的方法是完全一样的。使用哪个都可以


BeanUtils.populate(bean,Map),其中Map中的key必须与目标对象中的属性名相同,否则不能实现拷贝。


BeanUtils.copyProperties(newObject,oldObject),实现对象的拷贝


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,899评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,323评论 19 139
  • 对象的创建与销毁 Item 1: 使用static工厂方法,而不是构造函数创建对象:仅仅是创建对象的方法,并非Fa...
    孙小磊阅读 6,197评论 0 3
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 7,575评论 0 11
  • 集合的内容 集合框架 接口 实现 算法 collection 是所有集合接口的根接口,通用的集合操作 set 是不...
    惨不忍睹阅读 838评论 0 0