Java 反射机制
一、反射(Reflection)
1、反射的概念
反射是程序可以访问、检测和修改它本身状态或者行为的能力。通过反射,可以动态获取对象信息以及动态调用对象的方法。
反射的基础是因为在运行状态中,JVM能够知道对象的所有属性和方法,并且能够调用它的任意一个方法或访问其任一属性。
反射机制使得程序可以在运行时动态加载、查看和使用编译期间完全未知的类(对象)。
2、反射机制提供的功能
- 运行时检测对象的类型;
- 动态创建对象;
- 减速对象的属性和方法;
- 调用对象的任意方法;
- 修改构造器、方法和属性的可见性;
- 生成动态代理。
3、反射的通常用途
- 用于程序检查工具和调试器,它能获取程序在运行时刻的内部结构。只需要短短十几行代码,就可以遍历出对象所属类的结构,包括构造方法、声明的属性和定义的方法和修饰符等。
- 在运行时刻与注解配合,动态改变对象的行为,例如,为特定对象添加日志、权限控制等操作。
二、反射的实现
1、反射机制的实现基于4个类,他们分别描述了一个类的各个组成部分。
Class:代表运行时对象的类型信息,JVM使用这些类型信息确定方法。
Constructor:表示类的构造器,即构造方法的描述类。
Field:用来描述对象的属性集合。
Method:用来描述方法。
值得注意的是,通过上面方法访问反射机制创建的对象的私有构造器、属性、方法(少见)时,要先通过setAccessible(boolean T_or_F)方法修改访问权限!
2、Class类——反射的基石
Class类的对象用来描述运行时的类和接口,也用来表达enum、数组、基本数据类型以及关键词void。当一个类被加载或者当类加载器的defineClass()方法被调用,JVM就会自动产生一个Class对象。
要查看任何类,都必须获得一个Class对象。然后由Class对象调用反射APIs。
怎么获得Class对象?两种方式:
public class role{}
/*第一种*/
Class cls=Role.class;
/*第二种,forName()方法的参数必须是完整的路径类名(包名+类名),并且该方法需要捕获ClassNotFoundException异常。*/
Class cls=Class.forName(“cn.edu.ldu.javacourse.ch7.Role”);
在得到Class的实例对象后就可以创建Role类的实例了,newInstance()只能调用类的默认构造器生成对象,即如果类的构造函数是私有的,仍旧不能进行实例化:
/*newInstance()方法的缺点是只能调用默认构造函数*/
Object o=cls.newInstance();
Class的其他方法可以获取到类的构造方法、属性和成员方法:
- getConstructor();
- getField();
- getMethod();
同时这三个方法还有相应的getDeclaredxxx()版本,只获取该类自己声明的成员而不是从父类继承。
3、Constructor类——获取构造器
Constructor类是构造方法的描述类,Constructors对象可以通过Class对象的4个getXXX()方法获得(也可以获得构造器数组),这四个方法都需要抛出或捕获异常:
/*Class类有4个方法获得Constructors对象*/ Constructors con=cls.getXXXX();
/*获参数类型是String类型的构造方法*/ Constructor con=cls.getDeclaredConstructor(new Class[]{String.class}); /*然后设置所获得的构造器的访问权限*/ con. setAccessible(true); /*通过con的newInstance()方法创建新的对象*/ Object obj=con.newInstance (new Object[]{“Jerry”}); /*若要使用私有构造器,记得一定要修改访问权限*/
4、Field类——获取成员变量
成员变量用Field类进行封装,使用与上面相似。
/*返回了方法名*/
Field mem=cls.getDeclaredField("fieldName");
mem.setAccessible(true);
System.out.println("we get fields:"+mem.get(obj));
/*访问私有变量也要先改变访问权限*/
5、Method类——获取方法
封装方法的类是Method类,可以通过Class对象获取Method对象或对象数组,同样有4个方法。
基于反射获取并执行方法的过程:
Method me=cls.getMethod("methodBame",null);
Object name=me.invoke(obj,null);
6、反射处理数组
数组与所有对象一样,都通过Class的对象描述,也可使用标准getClass()方法获得数组的Class对象。
但是反射为普通类提供的构造函数访问不能用于数组,数组也没有可以访问的字段。
反射处理数组使用java.lang.reflect.Array类提供的静态方法,可以创建新的数组,可以获得数组对象的长度,以及读、写数组对象的索引值。注意,java.util.Arrays包含实用的方法用于排序数组,并将他们转换为集合。
反射机制创建数组:
/*第一个参数int.class指定数组类型,第二个参数声明数组中元素的个数。*/ int[] intArray=(int[]) Array.newInstance( int.class, 3); /*访问通过反射创建的数组*/ /*intArray参数是一个具体的数组*/ Array.get (intArray,0); Array.set (intArray,index,value);
思考,怎么重新调整现有数组大小?
思路,使用反射创建相同类型的新数组,然后在返回新数组之前,将旧数组中所有数据复制到新创建的数组。
public Object growArray(Object array,int size){ Class type=array.getClass().getComponentType(); Object grown= Array.newInstance(type,size); System.arraycopy(array,0,grown,0,Math.min(Array.getLength(array),size)); return grown; }