一、反射(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(); /*然后设置所获得的构造器的访问权限*/ 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类封装方法,基于反射获取并执行方法:
Method me=cls.getMethod("methodBame",null);
Object name=me.invoke(obj,null);
6、数组
数组与所有对象一样,都通过Class的对象描述,也可使用标准getClass()方法获得数组的Class对象。
但是数组不是类,没有构造函数也没有可访问的属性和方法。即反射为普通类提供的访问函数不能用于数组。
反射处理数组使用java.lang.reflect.Array类提供的静态方法,可以创建新的数组,可以获得数组对象的长度,以及读、写数组对象的索引值。
反射机制创建数组:
/*第一个参数int.class指定数组类型,第二个参数声明数组中元素的个数。*/ int[] intArray=(int[]) Array.newInstance( int.class, 3); /*访问通过反射创建的数组*/ /*intArray参数是一个具体的数组*/ Array.get (intArray,0); Array.set (intArray,index,value);
思考,怎么重新调整现有数组大小?