快捷搜索:

使用 Java Reflection

Reflection 是 Java 法度榜样开拓说话的特性之一,它容许运行中的 Java 法度榜样对自身进行反省,或者说“自审”,并能直接操作法度榜样的内部属性。例如,应用它能得到 Java 类中各成员的名称并显示出来。

Java 的这一能力在实际利用中大概用得不是很多,然则在其它的法度榜样设计说话中根本就不存在这一特点。例如,Pascal、C 或者 C++ 中就没有法子在法度榜样中得到函数定义相关的信息。

JavaBean 是 reflection 的实际利用之一,它能让一些对象可视化的操作软件组件。这些对象经由过程 reflection 动态的载入并取得 Java 组件(类) 的属性。

一个简单的例子

斟酌下面这个简单的例子,让我们看看 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 是用来描述某个类中单个措施的一个类。

开始应用 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 中定义的第一个措施的原型。

鄙人面的例子中,这三个步骤将为应用 reflection 处置惩罚特殊利用法度榜样供给例证。

模拟 instanceof 操作符

获得类信息之后,平日下一个步骤便是办理关于 Class 工具的一些基础的问题。例如,Class.isInstance 措施可以用于模拟 instanceof 操作符:

class A {}

public class instance1 {

public static void main(String args[])

{

try {

Class cls = Class.forName("A");

boolean b1

= cls.isInstance(new Integer(37));

System.out.println(b1);

boolean b2 = cls.isInstance(new A());

System.out.println(b2);

}

catch (Throwable e) {

System.err.println(e);

}

}

}

在这个例子中创建了一个 A 类的 Class 工具,然后反省一些工具是否是 A 的实例。Integer(37) 不是,但 new A() 是。

找出类的措施

找出一个类中定义了些什么措施,这是一个异常有代价也异常根基的 reflection 用法。下面的代码就实现了这一用法:

import java.lang.reflect.*;

public class method1 {

private int f1(Object p, int x) throws NullPointerException

{

if (p == null)

throw new NullPointerException();

return x;

}

public static void main(String args[])

{

try {

Class cls = Class.forName("method1");

Method methlist[]

= cls.getDeclaredMethods();

for (int i = 0; i 〈 methlist.length;

i++) {

Method m = methlist[i];

System.out.println("name

= " + m.getName());

System.out.println("decl class = " +

m.getDeclaringClass());

Class pvec[] = m.getParameterTypes();

for (int j = 0; j 〈 pvec.length; j++)

System.out.println("

param #" + j + " " + pvec[j]);

Class evec[] = m.getExceptionTypes();

for (int j = 0; j 〈 evec.length; j++)

System.out.println("exc #" + j

+ " " + evec[j]);

System.out.println("return type = " +

m.getReturnType());

System.out.println("-----");

}

}

catch (Throwable e) {

System.err.println(e);

}

}

}

这个法度榜样首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 工具,它们分手描述了定义在类中的每一个措施,包括 public 措施、protected 措施、package 措施和 private 措施等。假如你在法度榜样中应用 getMethods 来代替 getDeclaredMethods,你还能得到承袭来的各个措施的信息。

取得了 Method 工具列表之后,要显示这些措施的参数类型、非常类型和返回值类型等就不难了。这些类型是基础类型照样类类型,都可以由描述类的工具按顺序给出。

输出的结果如下:

name = f1

decl class = class method1

param #0 class java.lang.Object

param #1 int

exc #0 class java.lang.NullPointerException

return type = int

-----

name = main

decl class = class method1

param #0 class [Ljava.lang.String;

return type = void

-----

获取构造器信息

获取类构造器的用法与上述获取措施的用法类似,如:

import java.lang.reflect.*;

public class constructor1 {

public constructor1()

{

}

protected constructor1(int i, double d)

{

}

public static void main(String args[])

{

try {

Class cls = Class.forName("constructor1");

Constructor ctorlist[]

= cls.getDeclaredConstructors();

for (int i = 0; i 〈 ctorlist.length; i++) {

Constructor ct = ctorlist[i];

System.out.println("name

= " + ct.getName());

System.out.println("decl class = " +

ct.getDeclaringClass());

Class pvec[] = ct.getParameterTypes();

for (int j = 0; j 〈 pvec.length; j++)

System.out.println("param #"

+ j + " " + pvec[j]);

Class evec[] = ct.getExceptionTypes();

for (int j = 0; j 〈 evec.length; j++)

System.out.println(

"exc #" + j + " " + evec[j]);

System.out.println("-----");

}

}

catch (Throwable e) {

System.err.println(e);

}

}

}

这个例子中没能得到返回类型的相关信息,那是由于构造器没有返回类型。

这个法度榜样运行的结果是:

name = constructor1

decl class = class constructor1

-----

name = constructor1

decl class = class constructor1

param #0 int

param #1 double

-----

获取类的字段(域)

找出一个类中定义了哪些数据字段也是可能的,下面的代码就在干这个工作:

import java.lang.reflect.*;

public class field1 {

private double d;

public static final int i = 37;

String s = "testing";

public static void main(String args[])

{

try {

Class cls = Class.forName("field1");

Field fieldlist[]

= cls.getDeclaredFields();

for (int i

= 0; i 〈 fieldlist.length; i++) {

Field fld = fieldlist[i];

System.out.println("name

= " + fld.getName());

System.out.println("decl class = " +

fld.getDeclaringClass());

System.out.println("type

= " + fld.getType());

int mod = fld.getModifiers();

System.out.println("modifiers = " +

Modifier.toString(mod));

System.out.println("-----");

}

}

catch (Throwable e) {

System.err.println(e);

}

}

}

这个例子和前面那个例子异常相似。例中应用了一个新器械 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且应用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个法度榜样的输出是:

name = d

decl class = class field1

type = double

modifiers = private

-----

name = i

decl class = class field1

type = int

modifiers = public static final

-----

name = s

decl class = class field1

type = class java.lang.String

modifiers =

-----

和获取措施的环境一下,获取字段的时刻也可以只取得在当前类中申清楚明了的字段信息 (getDeclaredFields),或者也可以取得父类中定义的字段 (getFields) 。

根据措施的名称来履行措施

文本到这里,所举的例子无一例外都与若何获取类的信息有关。我们也可以用 reflection 来做一些其它的工作,比如履行一个指定了名称的措施。下面的示例演示了这一操作:

import java.lang.reflect.*;

public class method2 {

public int add(int a, int b)

{

return a + b;

}

public static void main(String args[])

{

try {

Class cls = Class.forName("method2");

Class partypes[] = new Class[2];

partypes[0] = Integer.TYPE;

partypes[1] = Integer.TYPE;

Method meth = cls.getMethod(

"add", partypes);

method2 methobj = new method2();

Object arglist[] = new Object[2];

arglist[0] = new Integer(37);

arglist[1] = new Integer(47);

Object retobj

= meth.invoke(methobj, arglist);

Integer retval = (Integer)retobj;

System.out.println(retval.intValue());

}

catch (Throwable e) {

System.err.println(e);

}

}

}

要是一个法度榜样在履行的某处的时刻才知道必要履行某个措施,这个措施的名称是在法度榜样的运行历程中指定的 (例如,JavaBean 开拓情况中就会做这样的事),那么上面的法度榜样演示了若何做到。

上例中,getMethod 用于查找一个具有两个整型参数且名为 add 的措施。找到该措施并创建了响应的 Method 工具之后,在精确的工具实例中履行它。履行该措施的时刻,必要供给一个参数列表,这在上例中是分手包装了整数 37 和 47 的两个 Integer 工具。履行措施的返回的同样是一个 Integer 工具,它封装了返回值 84。

创建新的工具

对付构造器,则不能像履行措施那样进行,由于履行一个构造器就意味着创建了一个新的工具 (准确的说,创建一个工具的历程包括分配内存和构造工具)。以是,与上例最相似的例子如下:

import java.lang.reflect.*;

public class constructor2 {

public constructor2()

{

}

public constructor2(int a, int b)

{

System.out.println(

"a = " + a + " b = " + b);

}

public static void main(String args[])

{

try {

Class cls = Class.forName("constructor2");

Class partypes[] = new Class[2];

partypes[0] = Integer.TYPE;

partypes[1] = Integer.TYPE;

Constructor ct

= cls.getConstructor(partypes);

Object arglist[] = new Object[2];

arglist[0] = new Integer(37);

arglist[1] = new Integer(47);

Object retobj = ct.newInstance(arglist);

}

catch (Throwable e) {

System.err.println(e);

}

}

}

根据指定的参数类型找到响应的构造函数并履行它,以创建一个新的工具实例。应用这种措施可以在法度榜样运行时动态地创建工具,而不是在编译的时刻创建工具,这一点异常有代价。

改变字段(域)的值

reflection 的还有一个用场便是改变工具数据字段的值。reflection 可以从正在运行的法度榜样中根据名称找到工具的字段并改变它,下面的例子可以阐明这一点:

import java.lang.reflect.*;

public class field2 {

public double d;

public static void main(String args[])

{

try {

Class cls = Class.forName("field2");

Field fld = cls.getField("d");

field2 f2obj = new field2();

System.out.println("d = " + f2obj.d);

fld.setDouble(f2obj, 12.34);

System.out.println("d = " + f2obj.d);

}

catch (Throwable e) {

System.err.println(e);

}

}

}

这个例子中,字段 d 的值被变为了 12.34。

应用数组

本文先容的 reflection 的着末一种用法是创建的操作数组。数组在 Java 说话中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。察看下面的例子看看数组是怎么事情的:

import java.lang.reflect.*;

public class array1 {

public static void main(String args[])

{

try {

Class cls = Class.forName(

"java.lang.String");

Object arr = Array.newInstance(cls, 10);

Array.set(arr, 5, "this is a test");

String s = (String)Array.get(arr, 5);

System.out.println(s);

}

catch (Throwable e) {

System.err.println(e);

}

}

}

例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,着末将这个字符串从数组中取得并打印了出来。

下面这段代码供给了一个更繁杂的例子:

import java.lang.reflect.*;

public class array2 {

public static void main(String args[])

{

int dims[] = new int[]{5, 10, 15};

Object arr

= Array.newInstance(Integer.TYPE, dims);

Object arrobj = Array.get(arr, 3);

Class cls =

arrobj.getClass().getComponentType();

System.out.println(cls);

arrobj = Array.get(arrobj, 5);

Array.setInt(arrobj, 10, 37);

int arrcast[][][] = (int[][][])arr;

System.out.println(arrcast[3][5][10]);

}

}

例中创建了一个 5 x 10 x 15 的整型数组,并为处于 [3][5][10] 的元素赋了值为 37。留意,多维数组实际上便是数组的数组,例如,第一个 Array.get 之后,arrobj 是一个 10 x 15 的数组。进而取得此中的一个元素,即长度为 15 的数组,并应用 Array.setInt 为它的第 10 个元素赋值。

留意创建数组时的类型是动态的,在编译时并不知道其类型。

小结

Java reflection 异常有用,它使类和数据布局能按名称动态检索相关信息,并容许在运行着的法度榜样中操作这些信息。Java 的这一特点异常强大年夜,并且是其它一些常说话,如 C、C++、Fortran 或者 Pascal 等都不具备的。

摘自:谋略机天下网   光阴:2003年9月10日

您可能还会对下面的文章感兴趣: