java中反射的概念文档格式.docx
《java中反射的概念文档格式.docx》由会员分享,可在线阅读,更多相关《java中反射的概念文档格式.docx(12页珍藏版)》请在冰点文库上搜索。
![java中反射的概念文档格式.docx](https://file1.bingdoc.com/fileroot1/2023-5/11/16c42a38-e0c9-4667-91c0-ab7aa8f765cb/16c42a38-e0c9-4667-91c0-ab7aa8f765cb1.gif)
按如下语句执行:
javaDumpMethodsjava.util.Stack
它的结果输出为:
publicjava.lang.Objectjava.util.Stack.push(java.lang.Object)
publicsynchronizedjava.lang.Objectjava.util.Stack.pop()
publicsynchronizedjava.lang.Objectjava.util.Stack.peek()
publicbooleanjava.util.Stack.empty()
publicsynchronizedintjava.util.Stack.search(java.lang.Object)
这样就列出了java.util.Stack类的各方法名以及它们的限制符和返回类型。
这个程序使用Class.forName载入指定的类,然后调用getDeclaredMethods来获取这个类中定义了的方法列表。
java.lang.reflect.Methods是用来描述某个类中单个方法的一个类。
1.2Java类反射中的主要方法
对于以下三类组件中的任何一类来说--构造函数、字段和方法--java.lang.Class提供四种独立的反射调用,以不同的方式来获得信息。
调用都遵循一种标准格式。
以下是用于查找构造函数的一组反射调用
lConstructorgetConstructor(Class[]params)--获得使用特殊的参数类型的公共构造函数,
lConstructor[]getConstructors()--获得类的所有公共构造函数
lConstructorgetDeclaredConstructor(Class[]params)--获得使用特定参数类型的构造函数(与接入级别无关)
lConstructor[]getDeclaredConstructors()--获得类的所有构造函数(与接入级别无关)
获得字段信息的Class反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:
lFieldgetField(Stringname)--获得命名的公共字段
lField[]getFields()--获得类的所有公共字段
lFieldgetDeclaredField(Stringname)--获得类声明的命名的字段
lField[]getDeclaredFields()--获得类声明的所有字段
用于获得方法信息函数:
lMethodgetMethod(Stringname,Class[]params)--使用特定的参数类型,获得命名的公共方法
lMethod[]getMethods()--获得类的所有公共方法
lMethodgetDeclaredMethod(Stringname,Class[]params)--使用特写的参数类型,获得类声明的命名的方法
lMethod[]getDeclaredMethods()--获得类声明的所有方法
1.3开始使用Reflection:
用于reflection的类,如Method,可以在java.lang.relfect包中找到。
使用这些类的时候必须要遵循三个步骤:
第一步是获得你想操作的类的java.lang.Class对象。
在运行中的Java程序中,用java.lang.Class类来描述类和接口等。
下面就是获得一个Class对象的方法之一:
Classc=Class.forName("
java.lang.String"
);
这条语句得到一个String类的类对象。
还有另一种方法,如下面的语句:
Classc=int.class;
或者
Classc=Integer.TYPE;
它们可获得基本类型的类信息。
其中后一种方法中访问的是基本类型的封装类(如Integer)中预先定义好的TYPE字段。
第二步是调用诸如getDeclaredMethods的方法,以取得该类中定义的所有方法的列表。
一旦取得这个信息,就可以进行第三步了——使用reflectionAPI来操作这些信息,如下面这段代码:
Methodm[]=c.getDeclaredMethods();
System.out.println(m[0].toString());
它将以文本方式打印出String中定义的第一个方法的原型。
2.处理对象:
如果要作一个开发工具像debugger之类的,你必须能发现filedvalues,以下是三个步骤:
a.创建一个Class对象
b.通过getField创建一个Field对象
c.调用Field.getXXX(Object)方法(XXX是Int,Float等,如果是对象就省略;
Object是指实例).
例如:
importjava.awt.*;
classSampleGet{
publicstaticvoidmain(String[]args){
Rectangler=newRectangle(100,325);
printHeight(r);
staticvoidprintHeight(Rectangler){
FieldheightField;
IntegerheightValue;
Classc=r.getClass();
heightField=c.getField("
height"
heightValue=(Integer)heightField.get(r);
System.out.println("
Height:
"
+heightValue.toString());
}catch(NoSuchFieldExceptione){
System.out.println(e);
}catch(SecurityExceptione){
}catch(IllegalAccessExceptione){
三、安全性和反射:
在处理反射时安全性是一个较复杂的问题。
反射经常由框架型代码使用,由于这一点,我们可能希望框架能够全面接入代码,无需考虑常规的接入限制。
但是,在其它情况下,不受控制的接入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。
Java编程语言定义一种多级别方法来处理反射的安全性。
基本模式是对反射实施与应用于源代码接入相同的限制:
n从任意位置到类公共组件的接入
n类自身外部无任何到私有组件的接入
n受保护和打包(缺省接入)组件的有限接入
不过至少有些时候,围绕这些限制还有一种简单的方法。
我们可以在我们所写的类中,扩展一个普通的基本类java.lang.reflect.AccessibleObject类。
这个类定义了一种setAccessible方法,使我们能够启动或关闭对这些类中其中一个类的实例的接入检测。
唯一的问题在于如果使用了安全性管理器,它将检测正在关闭接入检测的代码是否许可了这样做。
如果未许可,安全性管理器抛出一个例外。
下面是一段程序,在TwoString类的一个实例上使用反射来显示安全性正在运行:
publicclassReflectSecurity{
TwoStringts=newTwoString("
a"
"
b"
Fieldfield=clas.getDeclaredField("
m_s1"
//field.setAccessible(true);
Retrievedvalueis"
+
field.get(inst));
}catch(Exceptionex){
ex.printStackTrace(System.out);
如果我们编译这一程序时,不使用任何特定参数直接从命令行运行,它将在field.get(inst)调用中抛出一个IllegalAccessException异常。
如果我们不注释field.setAccessible(true)代码行,那么重新编译并重新运行该代码,它将编译成功。
最后,如果我们在命令行添加了JVM参数-Djava.security.manager以实现安全性管理器,它仍然将不能通过编译,除非我们定义了ReflectSecurity类的许可权限。
四、反射性能:
下面的程序是字段接入性能测试的一个例子,包括基本的测试方法。
每种方法测试字段接入的一种形式--accessSame与同一对象的成员字段协作,accessOther使用可直接接入的另一对象的字段,accessReflection使用可通过反射接入的另一对象的字段。
在每种情况下,方法执行相同的计算--循环中简单的加/乘顺序。
程序如下:
publicintaccessSame(intloops){
m_value=0;
for(intindex=0;
index<
loops;
index++){
m_value=(m_value+ADDITIVE_VALUE)*
MULTIPLIER_VALUE;
returnm_value;
publicintaccessReference(intloops){
TimingClasstiming=newTimingClass();
timing.m_value=(timing.m_value+ADDITIVE_VALUE)*
returntiming.m_value;
publicintaccessReflection(intloops)throwsException{
Fieldfield=TimingClass.class.
getDeclaredField("
m_value"
intvalue=(field.getInt(timing)+
ADDITIVE_VALUE)*MULTIPLIER_VALUE;
field.setInt(timing,value);
Errorusingreflection"
throwex;
Java语言反射提供一种动态链接程序组件的多功能方法。
它允许程序创建和控制任何类的对象(根据安全性限制),无需提前硬编码目标类。
这些特性使得反射特别适用于创建以非常普通的方式与对象协作的库。
例如,反射经常在持续存储对象为数据库、XML或其它外部格式的框架中使用。
Javareflection非常有用,它使类和数据结构能按名称动态检索相关信息,并允许在运行着的程序中操作这些信息。
但反射有两个缺点。
第一个是性能问题。
用于字段和方法接入时反射要远慢于直接代码。
性能问题的程度取决于程序中是如何使用反射的。
如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。
即使测试中最坏情况下的计时图显示的反射操作只耗用几微秒。
仅反射在性能关键的应用的核心逻辑中使用时性能问题才变得至关重要。
许多应用中更严重的一个缺点是使用反射会模糊程序内部实际要发生的事情。
程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。
反射代码比相应的直接代码更复杂,正如性能比较的代码实例中看到的一样。
解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方——记录其在目标类中的使用。
利用反射实现类的动态加载
首先定义一个接口来隔离类:
packageorg.bromon.reflect;
publicinterfaceOperator
{
publicjava.util.Listact(java.util.Listparams)
根据设计模式的原理,我们可以为不同的功能编写不同的类,每个类都继承Operator接口,客户端只需要针对Operator接口编程就可以避免很多麻烦。
比如这个类:
packageorg.bromon.reflect.*;
publicclassSuccessimplementsOperator
Listresult=newArrayList();
result.add(newString(“操作成功”));
returnresult;
很幸运,我使用的是Java,只有Java才提供这样的反射机制,或者说内省机制,可以实现我们的无理要求。
编写一个配置文件emp.properties:
#成功响应
1000=Success
#向客户发送普通文本消息
2000=Load
#客户向服务器发送普通文本消息
3000=Store
java.lang包中的Class类,java.lang.reflect包中的Field,Method,Constructor,Modifier类
首先要获取需要进行分析的类的名称,将类名称指定到Class类中的forName(Stringclassname)方法为参数,调用该方法后将返回一个该指定类型的Class对象,Class对象描述了该类型的详细信息,其中一些重要的方法包括:
getFields(),getMethods(),getConstructors()这些方法分别返回该类型中支持的公有字段,方法和构造器数组,其中包含超类的公有成员,而同是Class中以上的方法名称中加入了“Declared”的方法则返回该类型中定义的所有成员包括私有的但不包括超类成员,现在,我们已经获得了该类型的字段,方法和构造器数组,接着,我们使用java.lang.reflect包中的Field,Method,Constructor类来获得字段,方法和构造器的详细信息,例如:
我们要显示该类型中的所有的字段信息,伪代码:
//getDeclaredFields返回所有定义的字段数组
Field[]fields=Class.getDeclaredFields();
for(i=0;
fields.length;
Fieldf=fields[i];
Classtype=f.getType();
//getType()返回字段的数据类型
Stringname=f.getName();
//getName()返回字段名称
println(Modifier.toString(f.getModifiers()));
//getModifiers()返回一个代表访问控制符的整数
//Modifier.toString()将该整数转换为对应的访问控制符字符串
println("
+type.getName()+"
+name);
//type.getName()返回数据类型名称
好了下面看一个完整的例子,例子长了一点不过很简单!
importjavax.swing.*;
publicclassReflectTest
publicstaticvoidmain(String[]args)
{
Stringname;
if(args.length>
0)
name=args[0];
else
name=JOptionPane.showInputDialog("
输入一个类名:
按(java.lang.Double)格式"
try
Classcl=Class.forName(name);
Classsupercl=cl.getSuperclass();
//获得超类的Class对象
class"
//判断超类是否为空或为Object
if(supercl!
=null&
&
supercl!
=Object.class)
extends"
+supercl.getName());
System.out.print("
\n{\n"
printConstructors(cl);
System.out.println();
printMethods(cl);
printField(cl);
}"
//当使用class对象时注意捕获该异常,异常为类型未创建
catch(ClassNotFoundExceptione)
e.printStackTrace();
类型未创建"
System.exit(0);
/**
*该方法打印构造器的详细信息
*/
publicstaticvoidprintConstructors(Classcl)
//定义构造器数组
Constructor[]constructors=cl.getDeclaredConstructors();
constructors.length;
//将数组元素赋给一个构造器对象
Constructorconstr=constructors[i];
Stringname=constr.getName();
//获取构造器名称
//打印访问修饰符
System.out.print(Modifier.toString(constr.getModifiers()));
+name+"
("
*定义一个参数类型数组该数组是Class对象
*getParameterTypes()返回该构造器参数类型的数组
*打印参数类型名称
Class[]paramTypes=constr.getParameterTypes();
for(intj=0;
j<
paramTypes.length;
j++)
if(j>
0)System.out.print("
System.out.print(paramTypes[j].getName());
"
*该方法打印方法的详细信息
publicstaticvoidprintMethods(Classcl)
Method[]methods=cl.getDeclaredMethods();
methods.length;
Methodm=methods[i];
ClassretType=m.getReturnType();
Stringname=m.getName();
System.out.print(Modifier.toString(m.getModifiers()));
+retType.getName()+"
Class[]paramTypes=m.getParameterTypes();
*该方法打印字段的详细信息
publicstaticvoidprintField(Classcl)
Field[]fields=cl.getDeclaredFields();