感谢张龙老师的视频:http://www.verycd.com/topics/2838873/
在Java运行时环境中,对于任意一个类,通过反射能够知道这个类有哪些属性和方法,并且构造这个类的实例,对于任意一个类,能调用任意一个方法。这种动态地获取类的信息以及动态调用对象方法的功能来自于JAVA语言的反射(Reflection)机制。
反射机制主要提供了以下功能:
1、在运行时判断任意一个对象所属的类型
2、在运行时构造任意一个类的对象
3、在运行时判断任意一个类所具有的成员变量和方法
4、在运行时调用任意一个对象的方法
动态语言 :程序运行时,允许改变程序结构或者变量类型。Perl,Python,Ruby都是动态语言,而C#C++,JAVA都不是动态语言。
JAVA具有非常突出的动态的相关机制:Reflection,在java中是指可以用于在运行时加载、探知、使用编译期间完全未知的classes,换句话说,java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但是不包含methods定义),并生成该类的对象实体、或对其fields设值、或唤起其methods,这种“看透class”的能力被称为introspection。
JDK中,实现Reflection的类位于java.lang.Reflect包中:
Class类:代表一个类
Filed类:代表类的成员变量(成员变量也称为类的属性)
Method类:代表类的方法
Constructor:代表类的构造方法
Array类:提供了动态创建数组,以及访问数组的元素的静态方法
java的反射必须从Class类开始,Class类位于java.lang包中。
public class DumpMethods {
public int add(int a, int b) {
return a+b;
}
public void echo(String mesg) {
System.out.println(mesg);
}
/*
* 通过反射调用DumpMethods的add方法和echo方法
*/
public static void main(String[] args) {
try {
//首先通过类名获得该类的class对象,Class类是java反射的入口点
// Class<?> classType = Class.forName(args[0]);
//Class中的每一个方法都会对应一个Method实例
// Method[] methods = classType.getDeclaredMethods();
Class classType = DumpMethods.class;
//通过Class的newInstance()获得该Class对象代表的class的实例
Object dumpObj = classType.newInstance();
//下面两句代码同直接调用DumpMethod实例的add(100,200),通过反射调用add方法,首先要获得该add方法
//代表的Method对象,是利用Class类的getMethod方法,并且传递方法名和该方法的Class参数数组,只能获取public的方法实例。
//另外在调用该方法时,调用该Method对象的invoke方法,并将该方法所属的实例和参数的对象数组传入,返回一个Object对象
Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
Object resultObj = addMethod.invoke(dumpObj, new Object[]{100,200});
System.out.println(resultObj);
Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
Object obj = echoMethod.invoke(dumpObj, new Object[]{"zhutong is a good boy"});
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
注意Class对象的newInstance()只会调用Class对象对应的class的不带参数的构造方法,如果该class中的构造函数被重载了,那么直接使用Class对象的newInstance方法来构造实例是不成功的。
那我们还想得到Class的实例怎么办呢?和Class,Method类似,在一个类中,每个构造方法也对应了一个对象,即Constructor对象,可以通过Class的getConstructor (Class <?>... parameterTypes)
方法来获得Class对应类的某个构造方法,为什么是某个呢?构造方法重载仅仅是它们的参数不一样,可能是参数个数,参数类型以及参数顺序不一样,这里通过getConstructor (Class <?>... parameterTypes)
方法的参数中的Class数组来获得不同的Constructor对象,和获得Method对象一样,需要将构造方法的参数的类型构造为一个Class数组通过这个Class数组来判断获取的到底是哪个构造方法。那么问一个小问题,如果要通过getConstructor方法来调用不带参数的构造方法怎么办呢,很简单,传递一个空的Class数组不就行啦。
怎么通过Constructor来获得Class的实例呢?在Constructor中也包含了一个newInstance (Object ... initargs)
方法,但是该方法带有一个Object可变数组作为参数,当然就是传递给构造函数的参数啦,这里的Object可变参数的长度一定是与Class对象调用getConstructor方法中传递的Class可变参数的长度一致的,道理很简单,那么如何给空的构造方法传参数呢?呵呵,大家都懂的。。。
好了,通过反射现在可以在运行时得到类的实例,得到并且动态调用类的方法,以及得到带参数的构造方法并且进行实例化,那么我们可以得到类的字段吗?呵呵,当然可以,方法有Method对应,构造方法有Constructor对应,字段也有Field对应,Class中有两个方法:
Field |
getDeclaredField (String name) 通过字段名来获得该字段对应的Field对象
Field [] |
|
代码:
public class ReflectTester {
/*
* 利用反射机制动态生成一个对象的object的克隆对象
*/
public Object copy(Object object) throws Exception {
//获得对象所属的Class对象
Class classType = object.getClass();
System.out.println("class: " + classType);
//通过Class的getConstructor方法获得某个构造方法,并且调用newInstance()方法构造对象
Customer customer = (Customer) classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
//获得类的所有字段对应的Field对象
Field[] fields = classType.getDeclaredFields();
for(int i=0; i<fields.length; i++) {
Field field = fields[i];
String fieldName = field.getName();
String firstLetter = fieldName.substring(0, 1).toUpperCase();
//通过field.getName()获取字段的名字,并构造getXXX和setXXX方法
String getMethodStr = "get" + firstLetter + fieldName.substring(1);
String setMethodStr = "set" + firstLetter + fieldName.substring(1);
//通过调用Class的getMethod方法,并传入方法名和参数Class数组得到指定方法的Method对象
Method getMethod = classType.getMethod(getMethodStr, new Class[]{});
Method setMethod = classType.getMethod(setMethodStr, new Class[]{field.getType()});
//通过调用传入地对象object的getXXX方法获得指定的字段值
Object value = getMethod.invoke(object, new Object[]{});
System.out.println(fieldName + ":" + value);
//通过调用新创建的对象object的setXXX方法设置指定的字段值
setMethod.invoke(customer, new Object[]{value});
}
return customer;
}
public static void main(String[] args) throws Exception {
Customer customer = new Customer();
customer.setId(200807058);
customer.setAge(24);
customer.setName("张三");
ReflectTester rf = new ReflectTester();
Customer newCustomer = null;
newCustomer = (Customer) rf.copy(customer);
System.out.println(newCustomer.getId() + ":" + newCustomer.getName() + ":" + newCustomer.getAge());
}
}
class Customer {
/*
* 默认的构造方法不能通过Class的getConstructor动态获得
*/
public Customer() {
}
private long id;
private String name;
private int age;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}