Java反射
简介反射(Reflection) 是一种运行时检视(Inspect)类型信息并且可以动态操作类中的一切成员的一种机制。它常常用于如下一些场景中:
JDK中关于反射的相关类型都在 Class对象当jvm加载一个class文件到内存后,会为其创建一个唯一的Class对象(注意:同一个类加载器的前提)。所以我们可以理解为,Class对象就是一个class文件被加载到jvm中后的运行时表现。而反射的首要任务就是得到Class对象.得到Class对象的方法如下:
Class.forName()这种方法适合只知道类的字符串表示,也就是全称的情况.如果类还没有加载它还会加载此类,如果已经加载了就会返回已加载的class对象给你,比如: Class personCls = Class.forName("com.Person");
类的class字段这种方式是此类已经预先知道的情况下就非常合适,比如下面的代码 Class personCls = Person.class;
对于8个基本类型想获取其class对象信息除了可以用class字段的方式以外还可以利用其对应包装类型的TYPE字段来获取,比如: Class intCls = int.class;
对象的getClass方法这种方式适合于已经有此类对象的情况下来获取类的class对象信息,比如下面的代码 Person p = new Person();
Void类对象java方法的返回类型中有一个特殊的值就是void,使用java.lang.Void类来表示。所以获取此特殊的Class对象就用Void的class字段或TYPE字段来实现 Class<Void> clazz = Void.class;
Class对象的基本操作有了Class对象之后,我们就可以利用它来获取类中各种各样的信息,主要可以获取的信息有如下一些
获取类的名字类的名字分为简称和全称,比如下面的类其简称为Person,全称为com.Person package com;
通过反射获取类的名称方法如下: Class clazz = ...;
获取类的修饰符通过Class对象的getModifiers方法获取类的修饰符,此方法返回的是一个整数,然后依赖Modifier类的一系列方法来分析getModifiers方法返回整数的含义. int modifier = clazz.getModifiers();
获取包的信息可以通过Class对象对的getPackage()方法获取类的包信息,此方法返回的是Package类型,通过此Package类型就可以得到包的名字,比如: Package pkg = clazz.getPackage();
获取父类信息获取父类信息主要是靠getSuperClass()方法实现,如果当前的Class对象代表的是Object类型,接口类型,void类型,基本类型,那么此方法返回null值 Class<?> superClazz = clazz.getSuperclass();
获取实现的接口可以通过getInterfaces方法获取实现或继承的接口信息.如果当前的Class对象代表的是一个类,那么此方法得到是此类声明实现的所有接口信息,不包含其父类实现的接口信息.返回的数组中按声明的顺序排序.如果没有实现接口就返回长度为0的数组. 如果当前的Class对象代表的是一个接口,那么此方法返回的是此接口extends的所有接口信息,返回的数组中按照声明的接口顺序排序.如果没有继承任何接口,返回的数组是一个长度为0的数组. 如果当前的Class对象代表的是void或者基本类型,此方法返回长度为0的数组. 如果当前的Class对象代表的是数组类型,那么返回的是 Class<?>[] personInterfaces = clazz.getInterfaces();
获取构造函数通过getConstructors方法可以获取类的所有构造函数.比如下面的方法 Constructor<?>[] constructors = clazz.getConstructors();
获取字段字段分为类自己声明(Declare)的和从父类型继承过来的字段,想得到所有的字段(不区分是继承的还是自己声明的)就通过 Field[] fields = clazz.getDeclaredFields();
上面的方法可以得到任意修饰符的字段,静态与实例字段可以得到,public,private等访问修饰符的字段也可以得到.如果想得到某一个具体名字的字段可以通过 Field f = clazz.getDeclaredFields("字段名");
如果Class对象代表的类型没有字段或者代表的是数组类型,基本数据类型或者void类型会返回一个长度为0的数组. 返回数组中的元素并没有进行排序,并且并没有特定的顺序,比如按照声明的顺序,所以你的代码不能依赖反射中得到的字段的顺序来编写逻辑. 而getFields方法会返回自身和继承过来的所有public字段.并不包含其它修饰符的字段. 获取方法方法也分为类自己声明(Declare)的和从父类型继承过来的.可以通过 Method[] methods = clazz.getDeclaredMethods();
如果想获得特定的方法,就需要传递方法名与参数类型,因为方法有重载.比如下面的代码表示取得只有一个String类型参数的方法doSth. Method method = clazz.getDeclaredMethods("doSth",String.class)
getDeclaredMethods的第二个参数是可变长度的,因为方法的参数可以有多个. 如果一个类只有静态代码块,那么getDeclaredMethods返回的数组中不包括这个静态代码块. 如果Class对象代表的类型没有方法或者代表一个数组,那么返回的是长度为0的数组. 返回数组中的元素并没有进行排序,所以你的代码不能依赖反射中得到的方法的顺序来编写逻辑. 而getMethods方法会返回自身和继承过来的所有public方法.并不包含其它修饰符的方法. 实例化对象可以通过Class对象直接实例化一个类的对象出来.比如下面的代码 Class<Person> clazz = ...;
这个方法必须要求类有一个 如果没有无参的构造函数,就不能这样实例化对象了,只能靠反射先获取Constructor对象,然后通过Constructor来创建对象.
isInstance()Class对象的isInstance方法的作用等价于instanceof操作符.比如下面的代码会返回true Person person = new Person();
构造函数由于一个类可以有多个构造函数,所以反射API提供了getConstructors()方法得到所有的构造函数,用getConstructor(Class<?>… parameterTypes)来得到某一个具体的构造函数.比如下面的代码 Constructor<?>[] constructors = clazz.getConstructors();
如果没有对应参数类型的构造函数会抛出 有了Constructor对象之后,我们就可以调用调用其newInstance方法来实例化对象,其作用等价于new一个类的对象时指定调用这个构造函数. cons1.newInstance();
方法获得了Method对象后,我们可以获取方法的名字,可访问性以及调用方法等操作.比如下面的代码获取了方法的名字,可访问性 Method m = ...;
调用方法反射的方式调用方法主要是靠Method对象的 假设一个类中有下面的方法: public void doSth(String name) {
通过反射调用的方法如下: Class<Person> clazz = Person.class;
如果方法不能访问,仍然需要通过setAccessible来调整访问性.方法调用完毕之后再调回其访问性 Method m = clazz.getDeclaredMethod("doSth",String.class) ;
? 字段我们反射得到了某个Field对象之后,就可以得到此Field的名字,类型以及给字段赋值等操作.比如下面的代码就得到了字段的名字与类型 Field field = ...;
获取字段值获取字段值主要是靠 获取字段值分为获取静态与实例字段的值.如果是静态字段,调用上述方法获取字段时传递null,如果是实例字段的话,必须传递此字段所属类的对象给方法. 取得静态字段的值: Class<Person> clazz = Person.class;
取得实例字段的值 Class<Person> clazz = Person.class;
上面获取字段的值的方式,必须确保字段的修饰符是可以访问的,否则会抛出 如果字段的修饰符限制了访问,可以通过修改其访问性来获取字段值或者调用其对应的getter方法来获取字段的值.比如下面的代码就是修改修饰符的方式来获取字段的值 Field a = clazz.getDeclaredField("a");
修改可访问性只是设定是否跳过java语言的访问控制检查,并不是修改了字段的修饰符. 如果一个不能访问的字段,比如私有字段有对应的getter方法,那么你可以通过调用getter方法来获得字段的值.不过遗憾的是Field类型并没有提供获取此字段对应的getter的API. 设置字段值设置值与获取值是类似的,主要是通过set方法以及setDouble,setInt等方法来完成.比如下面的代码 Field a = clazz.getDeclaredField("a");
数组与反射数组是某个类型对象的一个合集,基于这个认识,数组的反射与普通的反射有一点点不同.比如下面的代码创建了一个长度为10的字符串数组. Class cls = Class.forName("java.lang.String");
其中Array类是在java.lang.reflection包下面的一个类型. 综合案例反射通常用在bean之间的拷贝,从Map中拷贝数据到bean中,以及把数据库中读取的记录转换为一个bean等等场景中. 从Map生成bean用过servlet的都知道,我们获取请求数据时需要把请求的数据封装到一个bean中,而Servlet中所有的请求的数据是放在一个map中的,基于这种情况,下面的案例是把一个map中的数据赋值给一个bean对象,map中键的名字作为bean的字段的名字,map中键对应的值作为对应字段的值.map的数据如下: Map<String,Object> map = new HashMap<>();
bean类的代码如下: public class UserInfoEntity {
实现把map中的数据拷贝到bean的代码如下,其中map中的age键在map中没有对应的字段,所以getDeclaredField时会抛出异常,但我们捕获了.所以并不影响最终bean实例的生成. public static void main(String[] args) throws Exception {
最终输出的结果是: UserInfoEntity [id=100,name=cj]
参考资料https://www.oracle.com/technetwork/articles/java/javareflection-1536171.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- Java – 一种了解集合是否有序的方法
- java – URL可以使用浏览器访问,但是仍然是具有URLConnecti
- java – OpenCV for Android – Mat.get(row,col,double []
- Java – 是否可以使用单个类扩展类的所有子类?
- java – 不使用Math.BigInteger的高功率模数
- 如何在JAVA中比较两个String对象
- java – 如何保护android中的sqlite db免遭被盗
- java – Android的位置.getAccuracy()返回1
- 详谈Java中BigDecimal的一个除法异常
- java – 防止Tomcat在HTTP错误状态4xx或5xx上干扰Jersey /