引言

反射这块内容在刚学习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类型的对象

java通过反射获取类的方法执行什么 java反射获取方法体的内容_字节码

利用反射最常见的地方在代理模式,代理模式的使用这里就不再多写