? ? ? ? ?经常有学弟学妹问我,反射有什么用。
? ? ? ? ??网上大神建议是反射尽量别用,反射在增加灵活性的同时会消耗一定资源。很多框架要用到它,因为框架需要利用配置文件进行动态配置,需要尽可能的灵活性,这时候就要牺牲一部分的性能了。
? ? ? ? ?记得我以前的帖子贴出过一个利用反射简单实现spring动态代理的代码:http://709002341.iteye.com/admin/blogs/2266317
? ? ? ? ?不过那只是个例子,前几天我真遇到一个要用反射实现的需求:
? ? ? ? ?场景是这样的:我是后端服务器,跟前端交互用的是后端的返回bean以json的形式传给前端,然后前端实现页面展示。但是前端前段时间换了个框架,然后一顿改,结果到我这出错了:如果我的返回bean有null值,在json串里是不体现那个属性的。比如我的bean有一个字符串str,如果str是null的话,返回的json串就没有str这个字段了,所以前台取str的时候出错。。。 ?老大让我改一下后端。
? ? ? ? 后端的场景是这样的,一个返回bean类似这样:
/**
* returnBean: 所有请求的返回bean
*
* @author xuejupo jpxue@travelsky.com create
*
*/
class ReturnBean {
/** 请求的返回状态,1为正常,0为错误 */
private int status = 0;
/** 当status为0的时候,errMsg表示错误信息 */
private String errMsg;
/** 请求返回的数据个数,当请求为查询的时候该字段有效 */
private int count = 0;
/** 请求返回的结果(这是最重要的请求结果,如果是查询请求,这里置入结果bean,如果是其他请求,这里置入其他的请求结果,如String) */
private Object object;
public final int getStatus() {
return status;
}
public final void setStatus(int status) {
this.status = status;
}
public final String getErrMsg() {
return errMsg;
}
public final void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
public final int getCount() {
return count;
}
public final void setCount(int count) {
this.count = count;
}
public final Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
? ? ? 所有的请求都是这一个bean返回,然后返回的数据bean部分封装到object里。
?
? ? ? ?比如我有一个bean:
/**
* MyBean: 我的查询返回bean
* @author xuejupo jpxue@travelsky.com
* create in 2016-1-29 下午2:13:54
*
*/
class MyBean {
/**
* 如果业务有需要,这种形式是最好的,给字段赋个初始值
*/
private String str1 = "";
private String str2;
private String str3;
private String str4;
public final String getStr1() {
return str1;
}
public final void setStr1(String str1) {
this.str1 = str1;
}
public final String getStr2() {
return str2;
}
public final void setStr2(String str2) {
this.str2 = str2;
}
public final String getStr3() {
return str3;
}
public final void setStr3(String str3) {
this.str3 = str3;
}
public final String getStr4() {
return str4;
}
public final void setStr4(String str4) {
this.str4 = str4;
}
}
? ? ? ?最好的方式当然是像str1一样,给所有的字段附上初始值。但是出现个问题,我把setObject改了个名字,想看看到底多少个数据bean需要修改(修改一下setObject名字,然后看文件出错个数即可),结果发现有差不多70多个bean需要修改。。。 ? 同样的工作重复70次,这是任何一个程序员都不能忍的。。。 ?所以我就想到修改setObject这个方法了。既然所有的数据bean都要用到这个方法,那我修改一下这个方法不就可以了?
? ? ? ?然后就想到反射了。
? ? ? ?把setObject修改为如下代码:
public void setObject(Object object) {
this.putEmptyIfNull(object);
this.object = object;
}
? ? ?增加一个方法,如果object中有属性为空,那么就初始化他:
/**
* putEmptyIfNull: 如果object中有属性为null,那么附空
* @param object
* void 返回类型
*/
private void putEmptyIfNull(Object object){
//获取对象的class类,请注意,执行完这一步,以下所有的操作都跟具体的对象无关了,只跟这个类有关,所以
//下边的赋值操作,要将对象object当作参数穿进去
Class<?> c = object.getClass();
Field[] fields = c.getDeclaredFields();
for(int i = 0; i < fields.length; i++){
Field f = fields[i];
//首先应该把这个字段赋成公开(public)
f.setAccessible(true);
try {
//如果对象object上该字段的值是null
if(f.get(object) == null){
//那么就将object对象上该值赋为空
f.set(object, "");
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e2) {
// TODO: handle exception
}
}
}
? ? ? ? 测试代码:
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
// TODO Auto-generated method stub
Test1 test = new Test1();
List<ReturnBean> l = new ArrayList<ReturnBean>();
long start = System.currentTimeMillis();
for(int i = 0; i < 1000; i++){
ReturnBean bean = test.getBean();
l.add(bean);
}
long end = System.currentTimeMillis();
System.out.println(((MyBean)l.get(0).getObject()).getStr3());
System.out.println("程序执行:"+(end - start));
}
? ? ?不用初始化null的时候的测试结果:
null 程序执行:3
?初始化null的测试结果:
程序执行:41
?可以看到,反射确实在增加灵活性上的基础上,降低了性能。不过很多时候这种性能上的消耗是可以忍受的(就像这个,1000个对象初始化才消耗41毫秒,这在对前端响应上是完全可以接收的),effect java上大神建议不要用反射,最大的原因还是一旦用反射,如果出错的话很难定位异常。所以,用反射的两大条件: ? 一是你一定要用,除了反射真的想不出其他好的办法了,二是,一定要做好注释(别看我上面没什么注释,其实我真实环境的注释那是杠杠的。。。)
原文:http://709002341.iteye.com/blog/2275135