反射是Java中比较高级的概念之一,有了反射的支持,我们可以开发出非常灵活的应用程序。例如我们根本不需要知道对象的类型,也不需要强制的将对象转化为特殊的类型,就可以调用对象上的方法,甚至我们可以调用本来无法访问到的方法(例如private方法)。同时我们也可以在程序运行的时候加载并且执行新的类库文件。
Java中Class的概念是所有反射的基础,有了Class我们才能够部根据Class的模板的特性来对具体的实例进行反射操作。
整体概念
Class是所有类的模版,既然是模板就要定义一切类所可能使用的内容。任何一个类都有属性,构造方法,普通方法。这些概念的定义都在java.lang.reflect包内定义:
Java.lang.reflect.Field: Class模板中属性的定义。
Java.lang.reflect.Method:Class模板中方法的定义。
Java.lang.reflcet.Constructor:Class模板中构造方法的定义。
如下是JDK1.4.2中java.lang.reflect包的结构:
如何获取对象的属性
有些时候我们可能需要在特殊的情况下需要在不知道对象具体类型的情况下来获取对象的属性。例如有如下代码:
package javame.reflection.prop;
public class SimpleBean {
private String name;
private int id;
public SimpleBean() {
}
public SimpleBean(String name, int id) {
this.name = name;
this.id = id;
}
}
如果我们需要访问SimpleBean的name属性和id属性时怎么办?这个时候反射就起到了作用,使用反射我们可以获取对象的属性列表,然后再用反射的方式来动态的获取实例的属性。具体参看如下代码:
package javame.reflection.prop;
import java.lang.reflect.Field;
public class PropertyGetter {
public static void main(String[] args) {
SimpleBean sb = new SimpleBean();
sb.setName("simple bean");
sb.setId(1);
showProperties( sb);
}
public static void showProperties(Object o) {
System.out.println("enter showProperties");
//获得对象的模板Class。
Class clazz = o.getClass();
//此方法用来获得所有public属性。
//Field[] fields = clazz.getFields();
//此方法用来获取所有的属性。
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
if (!fields[i].isAccessible()) {
//将方法的访问属性设置为可访问
fields[i].setAccessible(true);
}
//获取属性的名字。
String name = fields[i].getName();
Object value = null;
try {
//使用反射的方法获取属性值。
value = fields[i].get(o);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(name + ":" + value);
}
System.out.println("left showProperties");
}
}
上述代码得到如下输出:
enter showProperties
name:simple bean
id:1
left showProperties
通过上述代码可以看到,我们可以非常灵活的访问某一个对象的属性,甚至不需要知道对象的类型,更甚至可以访问private的属性。
如何调用对象方法
有些时候我们需要动态的决定调用的方法,那么我们怎么做呢?例如我们可嫩很想根据方法的名自来调用方法。
package javame.reflection.method;
public class SimpleBean {
public SimpleBean() {
}
public void methodOne() {
System.out.println("invocation of method one.");
}
private void methodTwo() {
System.out.println("invocation of method two.");
}
}
使用如下类调用SimpleBean的所有方法:
package javame.reflection.method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MethodInvocator {
public static void main(String[] args) {
SimpleBean sb = new SimpleBean();
invocateAllMethod(sb);
}
public static void invocateAllMethod(Object o) {
//取得对象类模板。
Class clazz = o.getClass();
//取得对象对象类模板声明的所有方法。
Method[] methods = clazz.getDeclaredMethods();
for(int i = 0; i < methods.length ; i++) {
Method method = methods[i];
//如果该方法不可访问,则设置访问属性为可访问。
if(!method.isAccessible()) {
method.setAccessible(true);
}
try {
//调用对象o上声明的该方法,调用过程使用null参数。
method.invoke(o, null);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
有如下输出:
invocation of method one.
invocation of method two.
如何使用构造方法创建对象
构造方法是对象的特殊方法,构造方法用来创建新的对象的实例,也可以以反射的方式使用构造方法来创建对象实例。
package javame.reflect.constructor;
public class SimpleBean {
private String name;
private int id;
public SimpleBean(String name, int id) {
super();
this.name = name;
this.id = id;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
package javame.reflect.constructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.IllegalAccessException;
import java.lang.InstantiationException;
public class BeanConstructor {
public static void main(String[] args) {
createNewInstance();
}
public static void createNewInstance() {
Class clazz = SimpleBean.class;
try {
Constructor constructor = clazz.getConstructor(new Class[]{String.class,
int.class});
SimpleBean sb = (SimpleBean)constructor.newInstance(
new Object[]{"simple bean", new Integer(1)});
System.out.println("simple bean name:" + sb.getName());
System.out.println("simple bean id:" + sb.getId());
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch(InstantiationException e) {
e.printStackTrace();
}
}
}
以上代码将有如下输出:
simple bean name:simple bean
simple bean id:1
使用Proxy来生成满足一定接口的动态类
通过使用Proxy可以动态的生成满足一定接口的动态类型。
例如有如下接口:
package javame.reflect.proxy;
public interface LogService {
public void log(String message);
}
我们可以使用一个特定的类通过实现LogService接口来提供一个特定的类来完成你需要的功能,只不过这些都依赖于开发时候开发出特定的类。但是通过使用Proxy来在程序运行的时候动态的生成实现LogService的接口的特殊类(Proxy)。
先实现一个InvocationHandler接口的类:
package javame.reflect.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("class name is:" + proxy.getClass().getName());
if(method.getName().equals("log")) {
String msg = (String) args[0];
System.out.println("message is:" + msg);
}
return null;
}
}
使用如下方法来生成动态的代理类来代理LogService来完成LogService的功能:
package javame.reflect.proxy;
import java.lang.reflect.Proxy;
public class LogProxy {
public static void main(String[] args) {
LogService log =
(LogService)Proxy.newProxyInstance(LogProxy.class.getClassLoader(),
new Class[]{LogService.class},new LogInvocationHandler() );
log.log("message");
if(Proxy.isProxyClass(log.getClass())) {
System.out.println("class name is:" + log.getClass().getName());
System.out.println("is a proxy class");
}
}
}
有如下结果:
class name is:$Proxy0
message is:message
class name is:$Proxy0
is a proxy class
实例分析
Commons-beanutils使用了大量的反射来完成bean相关的逻辑。以下将使用一个简单的方法实现对象间属性的拷贝:
//简单的Bean,用来测试属性拷贝。
package javame.reflection.beanutils;
import java.util.Date;
public class SimpleBean {
//三个属性,类型分别为String,int和
Date
private String name;
private int id;
private Date date;
public SimpleBean() {
super();
}
public SimpleBean(String name, int id, Date date) {
super();
this.name = name;
this.id = id;
this.date = date;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//使用如下类来拷贝对象属性
package javame.reflection.beanutils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class BeanUtils {
//属性拷贝方法,将source的所有属性拷贝到dist中。
public void copyProperties(Object dist, Object source) {
//如果源对象和目标对象不是相同类型则抛出异常。
if (dist.getClass() != source.getClass()) {
throw new IllegalArgumentException(
"only copy properties between two "
+ " of the same objects");
}
//获取对象类型
Class clazz = source.getClass();
//获取对象的所有声明的属性
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
String name = fields[i].getName();
//判断该属性是否是属性(是否有setter和getter方法)
if (this.isProperty(clazz, name)) {
try {
//将属性可访问性设为
true
if (!fields[i].isAccessible()) {
fields[i].setAccessible(true);
}
//在源对象上获取
Object sourceValue = fields[i].get(source);
fields[i].set(dist, sourceValue);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
// 判断是否为属性。
private boolean isProperty(Class clazz, String propName) {
if (this.hasGetter(clazz, propName) && this.hasGetter(clazz, propName)){
return true;
}
return false;
}
//判断类是否存在对propName的get方法。
private boolean hasGetter(Class clazz, String propName) {
String getName = this.getGetterName(propName);
Method method = null;
try {
method = clazz.getMethod(getName, null);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (method == null) {
return false;
}
return true;
}
//判断类是否存在对propName的set方法。
private boolean hasSetter(Class clazz, String propName) {
String setName = this.getSetterName(propName);
Method method = null;
try {
method = clazz.getMethod(setName, new Class[]
{ this.getFieldType(clazz, propName) });
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if (method == null) {
return false;
}
return true;
}
//获得propName指定字段的类型。
private Class getFieldType(Class clazz, String propName) {
Field field = null;
try {
field = clazz.getDeclaredField(propName);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
if (field == null) {
return null;
}
return field.getClass();
}
//获取propName字段的get方法名
private String getGetterName(String propName) {
return "get" + Character.toUpperCase(propName.charAt(0))
+ propName.substring(1);
}
//获取propName字段的set方法名
private String getSetterName(String propName) {
return "set" + Character.toUpperCase(propName.charAt(0))
+ propName.substring(1);
}
}
使用如下代码测试:
public static void main(String[] args) {
SimpleBean source = new SimpleBean("source bean", 1, new Date());
SimpleBean dist = new SimpleBean();
BeanUtils bu = new BeanUtils();
bu.copyProperties(dist, source);
System.out.println("dist name:" + dist.getName());
System.out.println("dist id:" + dist.getId());
System.out.println("dist date:" + dist.getDate());
}
得到如下输出:
dist name:source bean
dist id:1
dist date:Mon Jun 06 14:19:03 GMT+08:00 2005