反射机制
什么是反射机制
程序在运行时能够获取自身信息。(比如在java中,只要给定类的名字,就可以通过反射机制获取类的所有属性和方法)。
反射机制的作用
- 在运行时判断任意一个对象所属的类
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时任意调用一个对象的方法
- 在运行时构造任意一个类的对象
主要用于构造工具,而不是应用程序开发。
Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。
这个信息跟踪着每个对象所属的类。可以通过Class类访问这些信息。
获取Class实例
三种方法:
- Object getClass()
- Class.forName()
- T.class
/**
* 通过Object类中的getClass()
* @return Class对象
*/
private Class getClassInsatanceWayOne(){
Person p = new Person();
return p.getClass();
}
/**
* 通过Class的静态方法forName
* @return Class对象
* @throws ClassNotFoundException
*/
private Class getClassInstanceWayTwo() throws ClassNotFoundException {
return Class.forName("reflect.Person");
}
/**
* 通过T.class获取(T是任意Java类型)
* @return Class对象
*/
private Class getClassInstanceWayThree(){
return Person.class;
}
**注意:**一个Class对象实际上表示的是一个类型,而非一定是类。如int不是类,int.class是一个Class类型的对象。
Class实例的常见用法
getName
/**
* 通过Class实例获取类的名字
* @param clazz
* @return 类名
*/
private String getClassName(Class clazz){
return clazz.getName();
}
==
/**
* 比较两个类对象
* @param clazz
* @return true or false
*/
private boolean isPersonClass(Class clazz){
return clazz == Person.class;
}
newInstance
/**
* 通过Class的newInstance创建对象(调用无参数构造,若要通过有参构造创建使用Constructor的newInstance)
* @param clazz
* @return object
* @throws IllegalAccessException
* @throws InstantiationException
*/
private <T> T newInstance(Class clazz) throws IllegalAccessException, InstantiationException{
return (T) clazz.newInstance();
}
利用反射分析类的能力
检查类的结构
java.lang.reflect包下的Field
、Method
、Constructor
分别描述了类的域、方法和构造器。可以使用 Modifier
类中的 isPublic
、 isPrivate
或isFinal
判断方法或构造器是否是public
、private
或 final
。
Class类中的 getFields
、 getMethods
和getConstructors
方 法 将 分 别 返 回 类 提 供 的 public 域、 方法和构造器数组, 其中包括超类的公有成员。
Class 类的 getDeclareFields
、 getDeclareMethods
和getDeclaredConstructors
方法将分别返回类中声明的全部域、 方法和构 造器, 其中包括私有和受保护成员,但不包括超类的成员。
在运行时使用反射分析对象
/**
* 在运行时使用反射分析对象
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private void runtimeInstance() throws NoSuchFieldException, IllegalAccessException {
Person p = new Person("BB","88");
Class c1 = p.getClass();
Field f = c1.getDeclaredField("name");
//设置可以访问私有域
f.setAccessible(true);
//获取name属性的值
Object v = f.get(p);
System.out.println(v);
//设置name属性的值
f.set(p,"CC");
System.out.println(f.get(p));
}
使用反射编写泛型数组代码
将一个 Person[ ]临时地转换成 Object[ ] 数组, 然后 再把它转换回来是可以的,但一 从开始就是 Objectt[] 的数组却永远不能转换成 Person[]数组。
/**
* 扩充已填满的数组(运行时抛出类型转换错误)
* @param a
* @param newLength
* @return
*/
public static Object[] badCopy(Object[] a,int newLength){
Object[] newArray = new Object[newLength];
System.arraycopy(a,0,newLength,0,Math.min(a.length,newLength));
return newArray;
}
/**
* 通过反射,结合反射包下的Array来实现已满数组的扩容为可行方案
* @param a
* @param newLength
* @return
*/
public static Object goodCopyOf(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,newLength);
System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
return newArray;
}
调用任意方法
在 Method 类中有一个 invoke 方法, 它允许调用包装在当前 Method 对象中 的方法 。
public static void main(String[] args) throws Exception{
Method sqrt = Math.class.getMethod("sqrt",double.class);
printTable(1,10,10,sqrt);
}
public static void printTable(double from,double to,int n,Method f){
System.out.println(f);
double dx = (to-from)/(n-1);
for(double x = from;x<=to;x+=dx){
try {
double y = (Double) f.invoke(null,x);
System.out.printf("%10.4f | %10.4f%n",x,y);
}catch (Exception e){
e.printStackTrace();
}
}
}
参考
- [1]《Java核心技术卷I》
- [2] Hollis’s Blog