Java学习笔记--反射API
反射API
1.反射API的介绍
通过反射API可以获取Java程序在运行时刻的内部结构。比如Java类中包含的构造方法、域和方法等元素,并可以与这些元素进行交换。
按照 一般地面向对象的设计思路,一个对象的内部状态都应该通过相应的方法来改变,而不是直接去修改属性的值。一般Java类中的属性设置获取方法的命名都遵循JavaBeans规范中的要求,即利用setXxx和getXxx这样的的方法声明,因此可以实现一个使用工具类来完成对任意对象的属性设置和获取的操作,只要设置和获取属性的方法满足JavaBeans规范。
使用反射API设置对象的属性值的示例
public class ReflectSetter{
public static void invokeSetter(Object obj,String field,Object value) throws NoSuchMethodException,InvocationTargetException,IllegalAccessException{
String methodName = "set" + field.substring(0,1).toUpperCase() + field.substring();
Class<?> clazz = obj.getClass();
Method method = clazz.getMethod(methodName,value.getClass());
method.invole(obj,value);
}
}
从根本上来说,反射API实际上定义了一种功能提供者和使用之间的松散契约。以方法调用为例,按照Java语言的一般用法,在调用方法的时候,在代码中首先需要一个对象的变量作为调用的接受者,再把方法的名称直接写在代码中。方法的名称不可能是变量。编译器会检查这个对象中是否确实有待调用的方法,如果没有就会出现编译错误。这种一般地做法,是提供者和使用者之间的一种紧密的契约,由编译器来保证其合法性。而是用反射API,两者的契约只需要建立在名称和参数类型这个层次上就足够了。方法名称可以是变量,参数值也可以动态生成。调用的合法性由开发人员自己保证。如果方法调用不是合法的,相关的异常会在运行时抛出。
反射API的一个重要的使用场合是要调用的方法或者要操作的域的名称按照某种规律变化的时候。一个典型的场景就是在Servlet中用HTTP请求值来填充领域对象。比如在用户注册的时候,包含在HTTP请求中的用户所填写的相关信息,需要被填充到程序中定义好的领域对象中。只需要利用Servlet提供的API遍历请求中的所有参数,然后用ReflectSetter类中invokeSetter方法设置领域对象中与参数名称相对应的属性的值即可。另外一个场景是在数据库操作中,从SQL查询结果集中创建并填充领域对象。数据库的别名和领域对象属性名称也存在着类似的对应关系。
反射API在为Java程序带来灵活性的同时,也产生了额外的性能代价。由于反射API的实现机制,对于相同的操作,比如调用同一个方法,用反射API来动态实现比直接在源代码中编写的方式大概慢到一到两个数量级。随之Java虚拟机实现的改进,反射API的性能已经有了非常大的提升。但是这种性能的差距是客观存在。因此,在某些对性能要求比较高的应用,要慎用反射API。
2.使用反射API获取构造方法
通过反射API可以获取到Java类中的构造方法。通过构造方法可以在运行动态地创建Java对象,而不只是通过new操作符来进行创建。在得到Class类的对象之后,可以通过其中的方法来获取构造方法。相关的方法有4个,其中getConstructors用来获取所有的public构造方法的列表,getConstructor则根据参数类型来获取public的构造方法。另外两个对应方法getDeclaredConstructors和getDeclaredConstructor的作用类似。只不过它们会获取类中真正声明的构造方法,而忽略从父类中继承下来的构造方法。得到了表示构造函数的java.lang.reflect.Constructor对象之后,就可以获取关于构造方法的更多信息,以及通过newInstance方法创建出的新的对象。
一般地构造方法的获取和使用没有什么特殊之处,需要特别说明的是对参数长度可变的构造方法和嵌套类(nested class)的构造方法的使用。
如果构造方法声明了长度可变的参数,在获取构造方法的时候,要使用对应的数组类型的Class对象。这是因为长度可变的参数实际上是通过数组来实现的。
使用反射API获取参数可变的构造方法
public class VarargsConstructor{
public VarargsConstructor(String... names) {}
}
public void useVarargsConstructor() throws Exception{
Constructor<VarargsConstructor> constructor = VarargsConstructo.class.getDeclaredConstructor(String[].class);
//为了避免方法调用时的歧义,这样编译器就知道把这个字符串数组作为一个可变长度的参数来传递
//在调用newInstance的时候,要把作为实际参数的字符串数组先转换成Object类型,
constructor.newInstance((Object) new String[] {"A","B","C"});
}
对嵌套类的构造方法的获取,需要区分静态和非静态两种情况,即是否在声明嵌套类的时候使用static关键词。静态的嵌套类并没有特别之处,按照一般的方式来使用即可。而对非静态类嵌套类来说,其特殊之处在于它的对象实例中都有一个隐含的对象引用,指向包含它的外部类对象,也正是这个隐含的对象引用的存在,使得非静态类嵌套类中的代码可以直接引用外部类中的包含的私有域和方法。因此,在获取非静态类的构造方式时候,类型参数列表的第一个值必须是外部类的Class对象。
使用反射API获取嵌套类的构造方法
public class ConstNestedClass{
static class StaticNestedClass{
public StaticNestedClass(String name){}
}
class NestedClass{
public NestedClass(int count){}
}
public void useNestedClassConstrcutor() throws Exception{
Constructor<StaticNestedClass> sncc = StaticNestedClass.class.getDeclaredConstructor(String.class);
sncc.newInstance("Alex");
Constructor<NestedClass> ncc = NestedClass.class.getDeclaredConstructor(ConstructorUsage.class,int.class);
NestedClass nc = ncc.newInstance(this,3);
}
}
3.使用反射API获取域
通过反射API可以获取到类中公开的静态域和对象中的实例域。得到表示域的java.lang.reflect.Field类的对象之后,就可以获取和设置域的值。Class类中有4个方法用来获取域,分别是getFields、getField、getDeclaredFields和getDeclaredField,其含义与获取构造方法的4个方法类似。
使用反射API获取和使用静态域和实例域
public void useField() throws Exception{
Field fieldCount = FieldContainer.class.getDeclaredField("count");
fieldCount.set(null,3);
Field fieldName = FieldContainer.class.getDeclaredField("name");
fieldContainer fieldContainer = new FieldContainer();
fieldName.set(fieldContainer,"Arthur");
}
总的来说,对域的获取和设置都比较简单,但是只能对类中的公开域进行操作。私有域可以通过反射API的进行获取、操作(必须知晓字段名,且必须事先取消Java语言访问检查)。
4.使用反射API获取方法
最后一个可以通过反射API获取的元素是方法,这也是最常使用反射API的场景,即获取一个对象中的方法,并在运行时调用该方法。与之前提到的构造方法和域类似,Class类也有4个方法用来获取方法,分别是getMethods、getMethod、getDeclaredMethods和getDeclaredMethod。这4个方法的含义类似于获取构造方法和域的对应方法。在得到了表示方法的java.lang.reflect.Method类的对象之后,就可以查询该方法的详细信息,比如方法的参数和返回值的类型等。最重要的是可以通过invoke方法来传入实际参数并调用该方法。
使用反射API获取和使用公开和私有方法
public void useMethod() throws Exception{
MethodContainer mc = new MethodContainer();
Method publicMethod = MethodContainer.class.getDeclaredMethod("publicMethod");
publicMethod.invoke(mc);
Method privateMethod = MethodComtainer.class.getDeclareMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(mc);
}
5.使用反射API操作数组
使用反射API对数组进行操作的方式不同于一般的Java对象,是通过专门的java.lang.reflect.Array这个实用工具类来实现的。Array类提供的方法包括创建数组和操作数组中的元素。
使用反射API操作数组
public void useArray(){
String name = (String[])Array.mewInstance(String.class,10);
names[0] = "Hello";
Array.set(names,1,"World");
String str = (String)Array.get(names,0);
int[][][] matrix1 = (int[][][])Array.newInstance(int.class,3,3,3);
martix1[0][0][0] = 1;
int[][][] martix2 = (int[][][])Array.newInstance(int[].class,3,4);
martix2[0][0] = new int[10];
martix2[0][1] = new int[3];
martix2[0][0][1] = 1;
}
6.访问权限与异常处理
使用反射API的一个重要好处是可以绕过Java语言中默认的访问控制权限。比如正正常的代码中,一个类的对象是不能访问在另外一个类中声明的私有方法的,但是通过反射API是可以做到这一点。Constructor、Field和Method都继承自java.lang.reflect.AccessibleObject,其中的方法setAccessible可以用来设置是否绕开默认的权限。
在利用invoke方法来调用方法时,如果方法本身抛了异常,invoke方法会抛出InvocationTargetException异常来表示这种情况。在捕获到InvocationTargetException异常的时候,通过InvocationTargetExeption异常的getCause方法可以获取到真正的异常信息,帮助进行调试。
在处理与反射相关的异常的时候,可以直接捕获java.lang.ReflectiveOperationException,这是所有与反射操作相关的异常类的父类。
Java学习笔记--反射API的更多相关文章
- Java学习笔记之---API的应用
Java学习笔记之---API的应用 (一)Object类 java.lang.Object 类 Object 是类层次结构的根类.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个 ...
- Java学习笔记--反射
什么是Java反射 概念 java反射是指java能够在运行时确定类的类型信息,包括其方法.字段.构造函数等,并能够通过反射调用类或者类对象的方法.在Java中,java.lang.Class类与ja ...
- 0034 Java学习笔记-反射-初步2-操作对象
通过反射创建对象 通过反射创建对象有两种方式,一种通过Class对象的newInstance()方法,一种是获取到Class对象的Constructor后,再调用newInstance()方法,前者要 ...
- 0033 Java学习笔记-反射-初步1
先看看通过反射能干嘛 示例:修改对象的private实例变量 package testpack; import java.lang.reflect.Field; public class Test1 ...
- Java 学习笔记 反射与迭代器
反射 使用反射获得类 Class cls = Class.forName("全类名") //包括包名 Class cls = xx.Class;//xx代表类名 使用反射获得构造方 ...
- JAVA 学习笔记 - 反射机制
1. JAVA反射机制的概念 2. 怎样实例化一个 Class对象 Class.forName(包名.类名); 对象.getClass(); 类.class; ================== ...
- Java学习笔记——反射
反射就是把Java类中的各种成分映射成相应的java类. Class类-->java程序中的各个java类属于同一事物,描述这类事物的Java类名就是Class. Class.forName的作 ...
- Java学习笔记-反射机制
Java反射机制实在运行状态时,对于任意一个类,都能够知道这个类的属性和方法,对于任意一个对象,都能够调用他的任意一个属性和方法 获取Class对象的三种方式 Object类中的getClass()方 ...
- Java学习笔记二--API课堂记录
JavaSE课堂记录(二) 第一节课 方法的重载:方法名相同,参数列表不同 方法的重写:方法名,参数列表相同 两同:方法名相同,参数列表相同 两小:访问权限小与等于父类,返回值类型小于等于父类(返回值 ...
随机推荐
- 微信公众号开发笔记2(nodejs)
本篇主要记录调用微信各种api和功能实现 一.始于access_token 无论调用微信的什么api,都需要一个查询参数,就是我们每隔1小时或者2小时获取的access_token,笔记1中已经保证了 ...
- OC中Foundation框架之NSArray、NSMutableArray
NSArray概述 NSArray是OC中的数组类 NSArray特点 )只能存放任意OC对象,并且是有顺序的 )不能存放非OC对象,比如int/float/double/char/enum/stru ...
- [刷题]算法竞赛入门经典(第2版) 5-8/UVa230 - Borrowers
//又开学啦,不知不觉成为大二的老人了...时间过得好快啊,感觉好颓废... 题意:建立一个借书/归还系统.有借.还.把还的书插到书架上这三个指令. 代码:(Accepted, 0ms) //UVa2 ...
- 让人恼火的经历——手机H5网页被注入广告
你的网站是否在尾部出现了让人恼火的广告? 这次我算是遇到了这些流氓的广告.那么就让我们一步步攻克这些恼火的广告吧. 问题描述 某一天下午开始,我们制作的网站就开始被各种广告注入,类似上图这种. 还有在 ...
- springmvc缓存和mybatis缓存
首先要有一个搭建好的ssm框架,笔者使用的是基于maven搭建的ssm框架. 加入springmvc缓存: 导入相关依赖包: <dependency> <groupId>org ...
- mysql安装出现的问题
ERROR 1045 (28000): Access denied for user root@localhost (using password: NO) 错误描述: Mysql中添加用户之后可能出 ...
- iOS textfield 限制输入字数长度
iOS textfield限制输入的最大长度 [self.textFiled addTarget:self action:@selector(textFieldDidChange:) forContr ...
- 互联网金融P2P主业务场景自动化测试
互联网金融P2P行业,近三年来发展迅速,如火如荼. 据不完全统计,全国有3000+的企业. “互联网+”企业,几乎每天都会碰到一些奇奇怪怪的bug,作为在互联网企业工 ...
- 教育类APP开发现新增长,多款APP该如何突围?
"十二五"以来,国家共出台相关的重大教育政策文件741个,而进入到"十三五"时期教育领域综合改革深入推进的关键期,不断促进教育现代化的实现.加快迈入人力资源强国 ...
- 机器学习:Python实现聚类算法(一)之AP算法
1.算法简介 AP(Affinity Propagation)通常被翻译为近邻传播算法或者亲和力传播算法,是在2007年的Science杂志上提出的一种新的聚类算法.AP算法的基本思想是将全部数据点都 ...