??xml version="1.0" encoding="utf-8" standalone="yes"?>
cd.class;
对象?getClass();
Class.forName("c?); " com.wsq.UserBean"
import java.lang.reflect.Field;
public class B {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException {
Class clazz=A.class;
Field field=clazz.getDeclaredField("rubbish"); // 要是U有要用q个Ҏ(gu)
Field field1=clazz.getDeclaredField("rubbish1");
Field field2=clazz.getField("rubbish1"); //只能讉Kpublic
field.setAccessible(true);//要是U有q个很重?/span>
A obj=(A)clazz.newInstance();
System.out.println("U有"+field.get((obj)));
System.out.println("公有"+field1.get((obj)));
System.out.println(“公有"+field2.get((obj)));
}
}
反射的概忉|由Smith?982q首ơ提出的Q主要是指程序可以访问、检和修改它本w状态或行ؓ(f)的一U能力。这一概念的提出很快引发了计算机科学领域关于应用反性的研究。它首先被程序语a的设计领域所采用,q在Lisp和面向对象方面取得了成W。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava{就是基于反机制的语言。最q,反射机制也被应用C视窗pȝ、操作系l和文gpȝ中?nbsp;
反射本nq不是一个新概念Q它可能?x)我们联想到光学中的反概念,管计算机科学赋予了反射概念新的含义Q但是,从现象上来说Q它们确实有某些盔R之处,q些有助于我们的理解。在计算机科学领域,反射是指一cd用,它们能够自描q和自控制。也是_(d)q类应用通过采用某种机制来实现对自己行ؓ(f)的描qͼself-representationQ和监测QexaminationQ,q能Ҏ(gu)自n行ؓ(f)的状态和l果Q调整或修改应用所描述行ؓ(f)的状态和相关的语义。可以看出,同一般的反射概念相比Q计机U学领域的反不单单指反本w,q包括对反射l果所采取的措施。所有采用反机制的pȝQ即反射pȝQ都希望使系l的实现更开放。可以说Q实C反射机制的系l都h开放性,但具有开放性的pȝq不一定采用了反射机制Q开放性是反射pȝ的必要条件。一般来_(d)反射pȝ除了满开放性条件外q必L_因连接(Causally-connectedQ。所谓原因连接是指对反射pȝ自描q的改变能够立即反映到系l底层的实际状态和行ؓ(f)上的情况Q反之亦然。开放性和原因q接是反系l的两大基本要素?3700863760
Java中,反射是一U强大的工具。它使?zhn)能够创徏灉|的代码,q些代码可以在运行时装配Q无需在组件之间进行源代表链接。反允许我们在~写与执行时Q我们的程序代码能够接入装载到JVM中的cȝ内部信息Q而不是源代码中选定的类协作的代码。这使反成为构建灵zȝ应用的主要工兗但需注意的是Q如果用不当,反射的成本很高?/p>
Reflection ?nbsp;Java E序开发语a的特征之一Q它允许q行中的 Java E序对自w进行检查,或者说“自审”Qƈ能直接操作程序的内部属性。Java 的这一能力在实际应用中也许用得不是很多Q但是在其它的程序设计语a中根本就不存在这一Ҏ(gu)。例如,Pascal、C 或?nbsp;C++ 中就没有办法在程序中获得函数定义相关的信息?/p>
1Q检类Q?/strong>
1.1 reflection的工作机?/strong>
考虑下面q个单的例子Q让我们看看 reflection 是如何工作的?/p>
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
} catch (Throwable e) {
System.err.println(e);
}
}
}
按如下语句执行:(x)
java DumpMethods java.util.Stack
它的l果输出为:(x)
public java.lang.Object java.util.Stack.push(java.lang.Object)
public synchronized java.lang.Object java.util.Stack.pop()
public synchronized java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized int java.util.Stack.search(java.lang.Object)
q样列Zjava.util.Stack cȝ各方法名以及(qing)它们的限制符和返回类型?/p>
q个E序使用 Class.forName 载入指定的类Q然后调?nbsp;getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描q某个类中单个方法的一个类?/p>
1.2 Javacd中的主要方?/strong>
对于以下三类lg中的M一cL?nbsp;-- 构造函数、字D和Ҏ(gu) -- java.lang.Class 提供四种独立的反调用,以不同的方式来获得信息。调用都遵@一U标准格式。以下是用于查找构造函数的一l反调用:(x)
l Constructor getConstructor(Class[] params) -- 获得使用Ҏ(gu)的参数类型的公共构造函敎ͼ
l Constructor[] getConstructors() -- 获得cȝ所有公共构造函?/p>
l Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数cd的构造函?与接入别无?
l Constructor[] getDeclaredConstructors() -- 获得cȝ所有构造函?与接入别无?
获得字段信息的Class 反射调用不同于那些用于接入构造函数的调用Q在参数cd数组中用了字段名:(x)
l Field getField(String name) -- 获得命名的公共字D?/p>
l Field[] getFields() -- 获得cȝ所有公共字D?/p>
l Field getDeclaredField(String name) -- 获得cd明的命名的字D?/p>
l Field[] getDeclaredFields() -- 获得cd明的所有字D?nbsp;
用于获得Ҏ(gu)信息函数Q?/p>
l Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方?/p>
l Method[] getMethods() -- 获得cȝ所有公共方?/p>
l Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得cd明的命名的方?/p>
l Method[] getDeclaredMethods() -- 获得cd明的所有方?nbsp;
1.3开始?nbsp;ReflectionQ?/strong>
用于 reflection 的类Q如 MethodQ可以在 java.lang.relfect 包中扑ֈ。用这些类的时候必要遵@三个步骤Q第一步是获得你想操作的类?nbsp;java.lang.Class 对象。在q行中的 Java E序中,?nbsp;java.lang.Class cL描述cd接口{?/p>
下面是获得一?nbsp;Class 对象的方法之一Q?/p>
Class c = Class.forName("java.lang.String");
q条语句得到一?nbsp;String cȝcd象。还有另一U方法,如下面的语句Q?/p>
Class c = int.class;
或?/p>
Class c = Integer.TYPE;
它们可获得基本类型的cM息。其中后一U方法中讉K的是基本cd的封装类 (?nbsp;Integer) 中预先定义好?nbsp;TYPE 字段?/p>
W二步是调用诸如 getDeclaredMethods 的方法,以取得该cM定义的所有方法的列表?/p>
一旦取得这个信息,可以进行第三步了——?nbsp;reflection API 来操作这些信息,如下面这D代码:(x)
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
它将以文本方式打印出 String 中定义的W一个方法的原型?/p>
2Q处理对象:(x)
如果要作一个开发工具像debugger之类的,你必能发现filed values,以下是三个步?
a.创徏一个Class对象
b.通过getField 创徏一个Field对象
c.调用Field.getXXX(Object)Ҏ(gu)(XXX是Int,Float{,如果是对象就省略QObject是指实例).
例如Q?br />
import java.lang.reflect.*;
import java.awt.*;
class SampleGet {
public static void main(String[] args) {
Rectangle r = new Rectangle(100, 325);
printHeight(r);
}
static void printHeight(Rectangle r) {
Field heightField;
Integer heightValue;
Class c = r.getClass();
try {
heightField = c.getField("height");
heightValue = (Integer) heightField.get(r);
System.out.println("Height: " + heightValue.toString());
} catch (NoSuchFieldException e) {
System.out.println(e);
} catch (SecurityException e) {
System.out.println(e);
} catch (IllegalAccessException e) {
System.out.println(e);
}
}
}
在处理反时安全性是一个较复杂的问题。反经常由框架型代码用,׃q一点,我们可能希望框架能够全面接入代码Q无需考虑常规的接入限制。但是,在其它情况下Q不受控制的接入?x)带来严重的安全性风险,例如当代码在不值得信Q的代码共享的环境中运行时?/p>
׃q些互相矛盾的需求,Java~程语言定义一U多U别Ҏ(gu)来处理反的安全性。基本模式是对反实施与应用于源代码接入相同的限Ӟ(x)
n 从Q意位|到cdq件的接入
n c自w外部无M到私有组件的接入
n 受保护和打包Q缺省接入)lg的有限接?nbsp;
不过臛_有些时候,围绕q些限制q有一U简单的Ҏ(gu)。我们可以在我们所写的cMQ扩展一个普通的基本cjava.lang.reflect.AccessibleObject cR这个类定义了一UsetAccessibleҎ(gu)Q我们能够启动或关闭对q些cM其中一个类的实例的接入。唯一的问题在于如果用了安全性管理器Q它?yu)检正在关闭接入检的代码是否许可了这样做。如果未许可Q安全性管理器抛出一个例外?/p>
下面是一D늨序,在TwoString cȝ一个实例上使用反射来显C安全性正在运行:(x)
public class ReflectSecurity {
public static void main(String[] args) {
try {
TwoString ts = new TwoString("a", "b");
Field field = clas.getDeclaredField("m_s1");
// field.setAccessible(true);
System.out.println("Retrieved value is " +
field.get(inst));
} catch (Exception ex) {
ex.printStackTrace(System.out);
}
}
}
如果我们~译q一E序Ӟ不用Q何特定参数直接从命o(h)行运行,它将在field .get(inst)调用中抛Z个IllegalAccessException异常。如果我们不注释field.setAccessible(true)代码行,那么重新~译q新运行该代码Q它?yu)编译成功。最后,如果我们在命令行d了JVM参数-Djava.security.manager以实现安全性管理器Q它仍然不能通过~译Q除非我们定义了ReflectSecuritycȝ许可权限?/p>
反射是一U强大的工具Q但也存在一些不뀂一个主要的~点是对性能有媄响。用反基本上是一U解释操作,我们可以告诉JVMQ我们希望做什么ƈ且它满我们的要求。这cL作L慢于只直接执行相同的操作?/p>
下面的程序是字段接入性能试的一个例子,包括基本的测试方法。每U方法测试字D|入的一UŞ?nbsp;-- accessSame 与同一对象的成员字D协作,accessOther 使用可直接接入的另一对象的字D,accessReflection 使用可通过反射接入的另一对象的字Dc在每种情况下,Ҏ(gu)执行相同的计?nbsp;-- 循环中简单的?乘顺序?/p>
E序如下Q?/p>
public int accessSame(int loops) {
m_value = 0;
for (int index = 0; index < loops; index++) {
m_value = (m_value + ADDITIVE_VALUE) *
MULTIPLIER_VALUE;
}
return m_value;
}
public int accessReference(int loops) {
TimingClass timing = new TimingClass();
for (int index = 0; index < loops; index++) {
timing.m_value = (timing.m_value + ADDITIVE_VALUE) *
MULTIPLIER_VALUE;
}
return timing.m_value;
}
public int accessReflection(int loops) throws Exception {
TimingClass timing = new TimingClass();
try {
Field field = TimingClass.class.
getDeclaredField("m_value");
for (int index = 0; index < loops; index++) {
int value = (field.getInt(timing) +
ADDITIVE_VALUE) * MULTIPLIER_VALUE;
field.setInt(timing, value);
}
return timing.m_value;
} catch (Exception ex) {
System.out.println("Error using reflection");
throw ex;
}
}
在上面的例子中,试E序重复调用每种Ҏ(gu)Q用一个大循环敎ͼ从而^均多ơ调用的旉衡量l果。^均g不包括每U方法第一ơ调用的旉Q因此初始化旉不是l果中的一个因素。下面的图清楚的向我们展CZ每种Ҏ(gu)字段接入的时_(d)(x)
?nbsp;1Q字D|入时?nbsp;Q?br />
我们可以看出Q在前两副图?Sun JVM)Q用反的执行旉过使用直接接入?000倍以上。通过比较QIBM JVM可能E好一些,但反方法仍旧需要比其它Ҏ(gu)?00倍以上的旉。Q何JVM上其它两U方法之间时间方面无M显著差异Q但IBM JVM几乎比Sun JVM快一倍。最有可能的是这U差异反映了Sun Hot Spot JVM的专业优化,它在单基准方面表现得很糟p。反性能是Sun开?.4 JVM时关注的一个方面,它在反射Ҏ(gu)调用l果中显C。在q类操作的性能斚wQSun 1.4.1 JVM昄了比1.3.1版本很大的改q?/p>
如果Zؓ(f)创徏使用反射的对象编写了cM的计时测试程序,我们?x)发现这U情况下的差异不象字D和Ҏ(gu)调用情况下那么显著。用newInstance()调用创徏一个简单的java.lang.Object实例耗用的时间大U是在Sun 1.3.1 JVM上用new Object()?2倍,是在IBM 1.4.0 JVM的四倍,只是Sun 1.4.1 JVM上的两部。用Array.newInstance(type, size)创徏一个数l耗用的时间是M试的JVM上用new type[size]的两倍,随着数组大小的增加,差异逐步~小?/p>
Java语言反射提供一U动态链接程序组件的多功能方法。它允许E序创徏和控制Q何类的对?Ҏ(gu)安全性限?Q无需提前编码目标类。这些特性得反特别适用于创Z非常普通的方式与对象协作的库。例如,反射l常在持l存储对象ؓ(f)数据库、XML或其它外部格式的框架中用。Java reflection 非常有用Q它使类和数据结构能按名U动态检索相关信息,q允许在q行着的程序中操作q些信息。Java 的这一Ҏ(gu)非常强大,q且是其它一些常用语aQ如 C、C++、Fortran 或?nbsp;Pascal {都不具备的?/p>
但反有两个~点。第一个是性能问题。用于字D和Ҏ(gu)接入时反要q慢于直接代码。性能问题的程度取决于E序中是如何使用反射的。如果它作ؓ(f)E序q行中相对很涉?qing)的部分Q缓慢的性能不?x)是一个问题。即使测试中最坏情况下的计时图昄的反操作只耗用几微U。仅反射在性能关键的应用的核心逻辑中用时性能问题才变得至关重要?/p>
许多应用中更严重的一个缺Ҏ(gu)使用反射?x)模p程序内部实际要发生的事情。程序h员希望在源代码中看到E序的逻辑Q反等l过了源代码的技术会(x)带来l护问题。反代码比相应的直接代码更复杂Q正如性能比较的代码实例中看到的一栗解册些问题的最x案是保守C用反——仅在它可以真正增加灉|性的地方——记录其在目标类中的使用?/p>
利用反射实现c?/strong>的动态加?br />
Bromon原创 请尊重版?/p>
最q在成都写一?a>Ud增值项?/strong>Q俺负责后台server端。功能很单,手机用户通过GPRS打开Socket与服务器q接Q我则根据用户传q来的数据做出响应。做q?strong>c?/strong>似项目的兄弟一定都知道Q首先需要定义一?strong>c?/strong>gMSNP的通讯协议Q不q今天的话题是如何把q个pȝ设计得具有高度的扩展性。由于这个项目本w没有进行过较ؓ(f)完善的客h通和需求分析,所以以后肯定会(x)有很多功能上的扩展,通讯协议肯定?x)越来越庞大Q而我作ؓ(f)一个不那么勤快的hQ当然不想以后再M改写好的E序Q所以这个项目是实践面向对象设计的好Z(x)?/p>
首先定义一个接口来隔离c?/strong>Q?/p>
package org.bromon.reflect; public interface Operator { public java.util.List act(java.util.List params) } Ҏ(gu)设计模式的原理,我们可以Z同的功能~写不同?strong>c?/strong>Q每?strong>c?/strong>都承Operator接口Q客L(fng)只需要针对Operator接口~程可以避免很多麻烦。比如这?strong>c?/strong>Q?/p>
package org.bromon.reflect.*; public class Success implements Operator { public java.util.List act(java.util.List params) { List result=new ArrayList(); result.add(new String(“操作成功”)); return result; } } 我们q可以写其他很多c?/strong>Q但是有个问题,接口是无法实例化的,我们必须手动控制具体实例化哪?strong>c?/strong>Q这很不爽,如果能够向应用程序传递一个参敎ͼ让自己去选择实例化一?strong>c?/strong>Q执行它的actҎ(gu)Q那我们的工作就L多了?/p>
很幸q,我用的是JavaQ只有Java才提供这L(fng)反射机制Q或者说内省机制Q可以实现我们的无理要求。编写一个配|文件emp.properties: #成功响应 1000=Success #向客户发送普通文本消?/p>
2000=Load #客户向服务器发送普通文本消?/p>
3000=Store 文g中的键名是客户将发给我的消息_(d)客户发?000l我Q那么我执行Successc?/strong>的actҎ(gu)Q?strong>c?/strong>似的如果发?000l我Q那执行Loadc?/strong>的actҎ(gu)Q这样一来系l就完全W合开闭原则了Q如果要d新的功能Q完全不需要修改已有代码,只需要在配置文g中添加对应规则,然后~写新的c?/strong>Q实现actҎ(gu)okQ即使我弃这个项目而去Q它?yu)来也可以很好的扩展。这L(fng)pȝ具备了非常良好的扩展性和可插入性?/p>
下面q个例子体现了动态加载的功能Q程序在执行q程中才知道应该实例化哪?strong>c?/strong>Q?/p>
package org.bromon.reflect.*; import java.lang.reflect.*; public class TestReflect { //加蝲配置文g,查询消息头对应的c?/strong>?/p>
private String loadProtocal(String header) { String result=null; try { Properties prop=new Properties(); FileInputStream fis=new FileInputStream("emp.properties"); prop.load(fis); result=prop.getProperty(header); fis.close(); }catch(Exception e) { System.out.println(e); } return result; } //针对消息作出响应,利用反射导入对应?strong>c?/strong> public String response(String header,String content) { String result=null; String s=null; try { /* * 导入属性文件emp.properties,查询header所对应?strong>c?/strong>的名?/p>
* 通过反射机制动态加载匹配的c?/strong>,所有的c?/strong>都被Operator接口隔离 * 可以通过修改属性文件、添加新?strong>c?/strong>(l承MsgOperator接口)来扩展协?/p>
*/ s="org.bromon.reflect."+this.loadProtocal(header); //加蝲c?/strong> Class c=Class.forName(s); //创徏c?/strong>的事?/p>
Operator mo=(Operator)c.newInstance(); //构造参数列?/p>
Class params[]=new Class[1]; params[0]=Class.forName("java.util.List"); //查询actҎ(gu) Method m=c.getMethod("act",params); Object args[]=new Object[1]; args[0]=content; //调用Ҏ(gu)q且获得q回 Object returnObject=m.invoke(mo,args); }catch(Exception e) { System.out.println("Handler-response:"+e); } return result; } public static void main(String args[]) { TestReflect tr=new TestReflect(); tr.response(args[0],”消息内容”); } } 试一下:(x)java TestReflect 1000 q个E序是针对Operator~程的,所以无需做Q何修改,直接提供Load和Storec?/strong>Q就可以支持2000?000做参数的调用?/p>
有了q样的内省机Ӟ可以把接口的作用发挥到极臻I设计模式也更能体现出威力Q而不仅仅供我们饭后闲聊?/p>