引言
反射这块内容在刚学习java之初并没有接触到,在工作以后发现有很多地方都用到了反射,比如代理模式。所以还是需要深入学习一下反射的知识。
什么是反射?
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。通俗的来说,我们可以通过反射获得一个类所有的东西。如果我们单单是new一个对象出来,是并不能得到一个类所有的东西的,并且我们必须提前知道需要new的是一个什么对象。
反射有什么用?
反射能做的事情太多了,反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
如何使用反射?
获取字节码文件对象
想要使用反射,必须先获得该类的字节码文件对象,也就是.class文件,然后通过Class对象,我们就可以获得我们想要的该类的所有信息。一个类只对应一个.class文件,不会出现另一个.class文件与之对应。
获取字节码文件对象的方式有三种,分别是:
//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。
1、Class clazz1 = Class.forName("全限定类名");
//当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。
2、Class clazz2 = Person.class;
//通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段
3、Class clazz3 = p.getClass();
三种不同获取方式对应的类的阶段不同,所以我们可以根据阶段来选择创建的方式。
使用字节码文件对象
在得到了Class对象后,我们就可以通过Class对象来获取一个类的所有信息,我们可以获取该类的构造方法、成员对象、类方法、该类实现的所有接口等
1. 获取构造方法
获取构造方法主要有4种方式
方法 | 用途 |
getConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
来看一个具体的例子:
public class User implements IUser{
public User(){}
public User(int i){
System.out.printlin("constructor public");
}
private User(int i){
System.out.printlin("constructor private");
}
private int i;
public String str;
private int getInt(){
return i;
}
public void set(int i,String s){
this.i = i;
str = s;
}
public void setStr(String s){
str = s;
}
}
//首先获取字节码文件对象
Class clazz = Class.forName(Reflect.User);
//获取所有公有构造方法
Constructor[] constructor1 = clazz.getConstrucors();
//获取与参数匹配的构造方法
Constructor constructor2 = clazz.getDeclaredConstructor(int.class);
//获取所有的构造方法
Constructor[] constructor3 = clazz.getDeclaredConstructors();
//通过有参构造函数创建对象
User user2 = (User) constructor2.newInstance(12);
在上面的例子里,获取了三个构造方法对象,获取到构造方法对象后,我们就可以通过constructor的newInstance()方法来创建对象。当然,我们也可以直接使用无参的构造方法来创建对象。
2.获取方法
同理,我们可以利用Class对象来获取该类的所有方法,Class获取对象方法的方法如下:
方法 | 用途 |
getMethod(String name, Class…<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class…<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
//首先获取字节码文件对象
Class clazz = Class.forName(Reflect.User);
//获取所有公有方法
Method[] method1= clazz.getMethods();
//获取与参数匹配的方法
Method method2= clazz.getDeclaredMethod("set",int.class,String.class);
//获取所有的方法
Method [] method3 = clazz.getDeclaredConstructors();
//获取与参数匹配的某个公有方法
Method method2= clazz.getMethod("setStr",String.class);
获取到的Method对象可以实现很多功能,常用的Method类方法有:
静态方法名称 | 说明 |
getName() | 获取该方法的名称 |
getParameterType() | 按照声明顺序以 Class 数组的形式返回该方法各个参数的类型 |
getRetumType() | 以 Class 对象的形式获得该方法的返回值类型 |
getExceptionTypes() | 以 Class 数组的形式获得该方法可能抛出的异常类型 |
invoke(Object obj,Object…args) | 利用 args 参数执行指定对象 obj 中的该方法,返回值为 Object 类型 |
isVarArgs() | 查看该方法是否允许带有可变数量的参数,如果允许返回 true,否 则返回 false |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
3.获取成员变量
同理,我们可以利用Class对象来获取该类的所有成员对象,Class获取成员对象的方法如下:
方法 | 用途 |
getField(String name) | 获得该类某个公有的属性对象 |
getFields() | 获得该类所有公有的属性对象 |
getDeclaredField(String name) | 获得该类某个属性对象 |
getDeclaredFields() | 获得该类所有属性对象 |
Field类中的主要方法有:
方法名称 | 说明 |
get(Object obj) | 获取该方法的名称获得obj中对应的属性值 |
equals(Object obj) | 属性与obj相等则返回true |
set(Object obj, Object value) | 设置obj中对应属性值 |
3.其他重要方法
方法名称 | 说明 |
Class[] getInterfaces() | 确定此对象所表示的类或接口实现的接口返回值:接口的字节码文件对象的数组 |
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
反射在实际中的应用
java的反射机制就是增加程序的灵活性,避免将程序写死到代码里,
利用反射,在泛型为int的arryaList集合中存放一个String类型的对象
利用反射最常见的地方在代理模式,代理模式的使用这里就不再多写