Java反射技术

来源:互联网 发布:詹姆斯数据 编辑:程序博客网 时间:2024/05/19 01:10

反射就是把Java类中的各种成分映射成相应的java类.
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。

1.检测类:

1.1 reflection的工作机制

考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。

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); 


}

按如下语句执行:

java DumpMethods java.util.Stack

它的结果输出为:

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)

这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。

这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。

1.2 Java类反射中的主要方法

对于以下三类组件中的任何一类来说 -- 构造函数、字段和方法 -- java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用:

Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数,

Constructor[] getConstructors() -- 获得类的所有公共构造函数

Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)

Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)

获得字段信息的Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:

Field getField(String name) -- 获得命名的公共字段

Field[] getFields() -- 获得类的所有公共字段

Field getDeclaredField(String name) -- 获得类声明的命名的字段

Field[] getDeclaredFields() -- 获得类声明的所有字段

用于获得方法信息函数:

Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法

Method[] getMethods() -- 获得类的所有公共方法

Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法

Method[] getDeclaredMethods() -- 获得类声明的所有方法


1.3开始使用 Reflection:

用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。

下面就是获得一个 Class 对象的方法之一:

Class c = Class.forName("java.lang.String");

这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:

Class c = int.class;

或者

Class c = Integer.TYPE;

它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。

第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。

一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:

Class c = Class.forName("java.lang.String");

Method m[] = c.getDeclaredMethods();

System.out.println(m[0].toString());

它将以文本方式打印出 String 中定义的第一个方法的原型。

2.处理对象:

如果要作一个开发工具像debugger之类的,你必须能发现filed values,以下是三个步骤:

a.创建一个Class对象 
b.通过getField 创建一个Field对象 
c.调用Field.getXXX(Object)方法(XXX是Int,Float等,如果是对象就省略;Object是指实例).

例如: 
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); 


}

顾名思义,反射 (reflection) 机制就像是在吴承恩所写的西游记中所提及的「照妖镜」,可以让类别或对象 (object) 在执行时期「现出原形」。我们可以利用反射机制来深入了解某类(class) 的构造函数 (constructor)、方法 (method)、字段 (field),甚至可以改变字段的值、呼叫方法、建立新的对象。有了反射机制,程序员即使对所想使用的类别所知不多,也能照样写程序。反射机制能够用来呼叫方法,这正是反射机制能够取代函数指针的原因。

以 Java 来说,java.lang.reflect.Method (以下简称 Method) 类别是用来表示某类别的某方法。我们可以透过 java.lang.Class (以下简称 Class) 类别的许多方法来取得 Method 对象。Method 类别提供 invoke() 方法,透过 invoke(),此 Method 对象所表示的方法可以被呼叫,所有的参数则是被组织成一个数组,以方便传入 invoke()。

举个例子,下面是一个名为 Invoke 的程序,它会将命令列的 Java 类别名称和要呼叫的方法名称作为参数。为了简单起见,我假定此方法是静态的,且没有参数:

import java.lang.reflect.*;
class Invoke { 
public static void main(String[] args ) { 
try { 
Class c = Class.forName( args[0] ); 
Method m = c.getMethod( args[1], new Class [] { } ); 
Object ret = m.invoke( null, null ); 
System.out.println(args[0] + "." + args[1] +"() = " + ret ); 
} catch ( ClassNotFoundException ex ) { 
System.out.println("找不到此类别"); 
} catch (NoSuchMethodException ex ) { 
System.out.println("此方法不存在"); 
} catch (IllegalAccessException ex ) { 
System.out.println("没有权限调用此方法"); 
} catch (InvocationTargetException ex ) { 
System.out.println("调用此方法时发生下列例外:\n" + ex.getTargetException() ); 

}
}
我们可以执行 Invoke 来取得系统的时间:

java Invoke java.lang.System CurrentTimeMillis执行的结果如下所示:

java.lang.System.currentTimeMillis() = 1049551169474我们的第一步就是用名称去寻找指定的 Class。我们用类别名称 (命令列的第一个参数) 去呼叫 forName() 方法,然后用方法名称 (命令列的第二个参数) 去取得方法。getMethod() 方法有两个参数:第一个是方法名称 (命令列的第二个参数),第二个是 Class 对象的数组,这个阵例指明了方法的 signature (任何方法都可能会被多载,所以必须指定 signature 来分辨。) 因为我们的简单程序只呼叫没有参数的方法,我们建立一个 Class 对象的匿名空数组。如果我们想要呼叫有参数的方法,我们可以传递一个类别数组,数组的内容是各个类别的型态,依顺序排列。

一旦我们有了 Method 对象,就呼叫它的 invoke() 方法,这会造成我们的目标方法被调用,并且将结果以 Object 对象传回。如果要对此对象做其它额外的事,你必须将它转型为更精确的型态。

invoke() 方法的第一个参数就是我们想要呼叫目标方法的对象,如果该方法是静态的,就没有对象,所以我们把第一个参数设为 null,这就是我们范例中的情形。第二个参数是要传给目标方法作为参数的对象数组,它们的型态要符合呼叫 getMethod() 方法中所指定的型态。因为我们呼叫的方法没有参数,所以我们传递 null 作为 invoke() 的第二个参数。


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;

public class TestRef {
public static void main(String[] args) throws Exception {
TestRef testRef = new TestRef();
Class clazz = TestRef. class ;
System.out.println( " getPackage() = " + clazz.getPackage().getName());
// getModifiers()的返回值可以包含类的种类信息。比如是否为public,abstract,static 
int mod = clazz.getModifiers();
System.out.println( " Modifier.isAbstract(mod) = " + Modifier.isAbstract(mod));
System.out.println( " getName() = " + clazz.getName());
System.out.println( " getSuperclass() = " + clazz.getSuperclass().getName());
System.out.println( " getInterfaces() = " + clazz.getInterfaces()); // 实现了哪些Interface 
System.out.println( " clazz.getDeclaredClasses() = " + clazz.getDeclaredClasses()); // 包含哪些内部类 
System.out.println( " getDeclaringClass() = " + clazz.getDeclaringClass()); 
// 如果clazz是inner class 那么返回其outer class 

System.out.println( " ---------- " );
Constructor[] constructor = clazz.getDeclaredConstructors(); // 返回一组构造函数 Constructor[] 
if (constructor != null ) {
for ( int i = 0 ; i < constructor.length; i ++ ) {
System.out.println(constructor[i].getName());



System.out.println( " ---------- " );
Method[] method = clazz.getDeclaredMethods(); // Method[] 
if (method != null ) {
for ( int i = 0 ; i < method.length; i ++ ) {
System.out.println(method[i].getName());



System.out.println( " ---------- " );
Field[] field = clazz.getDeclaredFields(); // Field[] 
if (field != null ) {
for ( int i = 0 ; i < field.length; i ++ ) {
System.out.println(field[i].getName());
System.out.println(field[i].getType().getName());
System.out.println(field[i].get(testRef));



// 动态生成instance(无参数) 
Class clz = Class.forName( " reflection.TestRef " );
Object obj = clz.newInstance();
System.out.println(((TestRef)obj).getStr());

// 动态生成instance(有参数) 
Class[] params = new Class[] {String. class , int . class , double . class } ;
Constructor construct = clz.getConstructor(params);
// JDK1.5的情况下可以直接用{"haha",999,100.01}作为参数 
Object obj2 = construct.newInstance(new Object[]{"haha",new Integer( 999 ), new Double( 100.01 )} ); 
System.out.println(((TestRef)obj2).getStr());

// 动态调用method(public method) 
Class[] params2 = new Class[] {String. class } ;
Method methods = clz.getMethod( " setStr " , params2);
methods.invoke(testRef, new Object[] { " invoke method " } );
System.out.println(testRef.getStr());

// 动态改变field内容(public field) 
Field fields = clz.getField( " str " );
fields.set(testRef, " set field's value " );
System.out.println(testRef.getStr());

}

public TestRef() {
System.out.println( " --- complete TestRef() --- " );


public TestRef(String str, int i, double d) {
this .str = str;
this .i = i;
this .d = d;
System.out.println( " --- complete TestRef(String str, int i, double d) --- " );


public String str = " I'm a string " ;

int i = 1 ;

double d = 3.14 ;

HashMap map = new HashMap();

public double getD() {
return d;
}

public void setD( double d) {
this .d = d;
}

public int getI() {
return i;
}

public void setI( int i) {
this .i = i;
}

public HashMap getMap() {
return map;
}

public void setMap(HashMap map) {
this .map = map;
}

public String getStr() {
return str;
}

public void setStr(String str) {
this .str = str;

}

1 0