(十)反射
能够分析类能力的程序称为反射。
Class类
程序运行中,系统为每个对象维护一个运行时的类标识,该信息可以追踪对象所属的类。可以通过专门的java类访问这些信息,保存这些信息的类称为Class类。
获取类型标识:
Employee e;
Class c1 = e.getClass();
获取类名对应的Class对象
String className = "java.util.Date";
Class c2 = Class.forName(className);
通过Class类实例化一个对象
e.getClass().newInstance();
将会调用e所属的类的默认构造器。如果需要为构造器提供参数,使用Constructor类的newInstance方法。
虚拟机为每个类管理一个Class对象,因此可以使用==来比较两个Class对象,由此判断两个对象是否属于同一个类
e.getClass()==g.getClass()
注:getClass和instanceof的区别见这里。
利用反射分析类的能力
检查类的结构
java.lang.reflect包中有三个类Field,Method,Constructor分别描述类的域,方法和构造器。
Class类中的getFields,getMethods,getConstructor方法返回类提供的public域,方法和构造器的数组,包括超类的公有成员。
Class类中的getDeclareFields,getDeclareMethods,getDeclareConstructor返回类中声明的全部域,方法和构造器的数组,包括私有和保护的成员,不包括超类的成员。
在运行时使用反射分析对象
除了可以查看类的结构外,还可以查看数据域的实际内容。
查看对象域的关键方法是Field的get方法。如果f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将返回一个对象,其值为obj域的当前值。
举例:
Employee harry = new Employee("Harry",3500,10,1,1989);
Class c1 = harry.getClass();
Field f = c1.getDeclaredField("name");
Object v = f.get(harry);
使用反射编写泛型数组代码
java.lang.reflect的Array类允许动态创建数组。
Class的getComponentType方法可以获取数组内元素的类型。
下面,我们使用反射编写Arrays的copyOf方法:
以下为该方法的解释
static <T> T[]
copyOf(T[] original, int newLength)
Copies the specified array, truncating or padding with nulls (if necessary) so the copy has the specified length.
该方法将原来的对象数组拷贝到一个新的对象数组中,对象类型不确定。
public static Object copyOf(Object a, int newLength)
首先,我们需要通过Array类的newInstance动态创建一个新的数组,由于数组元素类型不确定,需要使用反射机制获取
Class c1 = a.getClass();
Class componentType = c1.getComponentType();
接着,获取原来的数组长度
int length = Array.getLength(a);
其次,通过componentType和length创建数组对象
Object newArray = Array.newInstance(componentType,length);
最后,执行数组的复制
System.arraycopy(a,0,newArray,Match.min(length,newLength));
完整代码:
public static Object copyOf(Object a, int newLength){ Class c1 = a.getClass(); if(!c1.isArray()) return null; Class componentType = c1.getComponentType(); int length = Array.getLength(a); Object newArray = Array.newInstance(componentType,length); System.arraycopy(a,0,newArray,Match.min(length,newLength)); return newArray; }
调用任意方法
Method类的invoke方法,它允许调用包装在当前Method对象中的方法。
比如m1代表Employee类的getName方法,harry是Employee对象:
String n = (String)m1.invoke(harry);
详细信息看这里。