在项目进行单元测试的时候,用到了反射,结合这篇文章《JAVA反射机制 》进行了学习。http://blog.csdn.net/justinavril/article/details/2873664,有个别地方进行了补充。
Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。 Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。
JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。
1. 一个简单的例子
考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) {
try {
//获得一个 Class 对象
Class c = Class.forName("java.util.Stack");
//取得该类中定义的所有方法(包含private方法)
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);
}
}
}它的结果输出为: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 c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString()); 它将以文本方式打印出 String 中定义的第一个方法的原型。class S {
}
public class IsInstance {
public static void main(String args[]) {
try {
Class cls = Class.forName("S");
boolean b1 = cls.isInstance(new Integer(37));
System.out.println(b1);
boolean b2 = cls.isInstance(new S());
System.out.println(b2);
} catch (Throwable e) {
System.err.println(e);
}
Class c = Double.TYPE;
}
}它的结果输出为:false true在这个例子中创建了一个S 类的 Class 对象,然后检查一些对象是否是S的实例。Integer(37) 不是,但 new S()是。
import java.lang.reflect.Method;
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);
}
}
}输出的结果如下: 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 -----这个程序首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你还能获得继承来的各个方法的信息。
import java.lang.reflect.Constructor;
public class Constructor1 {
public Constructor1() {
}
protected Constructor1(int i, double d) throws Exception {
}
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 exc #0 class java.lang.Exception -----5.获取类的字段(域)
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
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");
//获得该类中定义的所有字段(包含private字段)
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);
}
}
} 这个程序的输出是: 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 = -----这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。
import java.lang.reflect.Method;
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数组
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
// 利用Class对象的getMethod方法,根据方法名、参数类型数组获得Method对象
Method meth = cls.getMethod("add", partypes);
Method2 methobj = new Method2();
//声明存放实际参数的Object数组
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
//利用Method对象的invoke方法,执行add方法,
//参数1:方法所在类的实际对象,参数2:方法的实际参数数组,返回值为要执行的add方法的返回值
Object retobj = meth.invoke(methobj, arglist);
Integer retval = (Integer) retobj;
System.out.println(retval.intValue());
}
catch (Throwable e) {
System.err.println(e);
}
}
}这个程序运行的结果是: 84假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。
import java.lang.reflect.Constructor;
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数组
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
//根据指定的参数类型获得相应的构造函数的Constructor对象
Constructor ct = cls.getConstructor(partypes);
//声明存放实际参数的Object数组,指定构造函数的实际参数
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);
}
}
} 这个程序运行的结果是:a = 37 b = 47根据指定的参数类型找到相应的构造函数并执行它,以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点非常有价值。
import java.lang.reflect.Field;
public class Field2 {
public double d=1.11;
public String name="ZhangSan";
public static void main(String args[]) {
try {
Class cls = Class.forName("Field2");
Field2 fobj = new Field2();
System.out.println("d = " + fobj.d);
System.out.println("d = " + fobj.name);
//根据字段名获得字段对象
Field fld = cls.getField("d");
//更改字段的值。参数1:该字段所在类的对象,参数2:该字段的新值
fld.setDouble(fobj, 12.34);
//根据字段名获得字段对象
Field flname = cls.getField("name");
//更改字段的值。参数1:该字段所在类的对象,参数2:该字段的新值
flname.set(fobj, "LiSi");
System.out.println("d = " + fobj.d);
System.out.println("d = " + fobj.name);
}
catch (Throwable e) {
System.err.println(e);
}
}
} 这个程序运行的结果是:d = 1.11 d = ZhangSan d = 12.34 d = LiSi9.使用数组
import java.lang.reflect.Array;
public class Array1 {
public static void main(String args[]) {
try {
Class cls = Class.forName("java.lang.String");
System.out.println(cls.getName());
//创建一个长度为10的数组
Object arr = Array.newInstance(cls, 10);
//给数组第6个元素赋值
Array.set(arr, 5, "this is a test");
//获得数组中第6个元素中存放的值
String s = (String) Array.get(arr, 5);
System.out.println(s);
} catch (Throwable e) {
System.err.println(e);
}
}
}这个程序运行的结果是:java.lang.String this is a test例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。
import java.lang.reflect.Array;
public class Array2 {
public static void main(String args[]) {
//创建了一个 5 x 10 x 15 的整型数组
int dims[] = new int[] { 5, 10, 15 };
Object arr = Array.newInstance(Integer.TYPE, dims);
System.out.println(arr.getClass().getComponentType());
//获得 3 x 10 x 15处的整型数组
Object arrobj = Array.get(arr, 3);
System.out.println(arrobj.getClass().getComponentType());
//获得 3 x 5 x 15处的整型数组
arrobj = Array.get(arrobj, 5);
//在 3 x 5 x 15处的整型数组里,下标为10的元素里的值设为37
Array.setInt(arrobj, 10, 37);
System.out.println(arrobj.getClass().getComponentType());
int arrcast[][][] = (int[][][]) arr;
System.out.println(arrcast[3][5][10]);
}
}这个程序运行的结果是:class [[I class [I int 37例中创建了一个 5 x 10 x 15 的整型数组,并为处于 [3][5][10] 的元素赋了值为 37。注意,多维数组实际上就是数组的数组,例如,第一个 Array.get 之后,arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素,即长度为 15 的数组,并使用 Array.setInt 为它的第 10 个元素赋值。
public class Student {
private String stuName=null;
public void sayName(){
System.out.println("My name is "+stuName.toUpperCase());
}
private String sayNameAge(String name, int age){
return ("My name is "+name+", My age "+age);
}
}下面是调用方式:import java.lang.reflect.Field;
//利用反射,调用类的私有字段,并设置新值
public class Field3 {
public static void main(String args[]) {
try {
Class cls = Class.forName("Student");
Student stuObj=new Student();
//根据字段名获得字段对象,也可以用getField
Field fld = cls.getDeclaredField("stuName");
//设置该字段的可访问性
fld.setAccessible(true);
//更改私有字段的值。参数1:该字段所在类的对象,参数2:该字段的新值
fld.set(stuObj, "Zhang San");
stuObj.sayName();
}
catch (Throwable e) {
System.err.println(e);
}
}
} 这个程序运行的结果是:My name is ZHANG SAN通过setAccessible方法,把私有字段stuName的访问属性,该为public。如果私有变量stuName没有被初始化,则在调用stuName.toUpperCase()方法时就出错。所以在此种情况下,可以通过反射的形式,让私有变量也可以访问和初始化。
import java.lang.reflect.Field;
//利用反射,调用类的私有方法
public class Method3 {
public static void main(String args[]) {
try {
Class cls = Class.forName("Student");
//声明一个存放参数类型的Class数组
Class partypes[] = new Class[2];
partypes[0] = String.class;
partypes[1] = Integer.TYPE;
// 利用Class对象的getMethod方法,根据方法名、参数类型数组获得Method对象
Method meth = cls.getDeclaredMethod("sayNameAge",partypes);
Student stuObj=new Student();
//设置该方法的可访问性
meth.setAccessible(true);
//声明存放实际参数的Object数组
Object arglist[] = new Object[2];
arglist[0] = new String("ZhangSan");
arglist[1] = new Integer(150);
//利用Method对象的invoke方法,执行say方法,
//参数1:方法所在类的实际对象,参数2:方法的实际参数数组,返回值为要执行的add方法结果
Object retobj = meth.invoke(stuObj, arglist);
System.out.println(retobj);
}
catch (Throwable e) {
System.err.println(e);
}
}
} 这个程序运行的结果是:My name is ZhangSan, My age 150首先声明一个参数类型的Class对象数组,然后利用此数组和方法名,调用Class对象的getDeclaredMethod方法生成Method对象,并设置可访问性,然后定义方法实参Object对象数组,利用此实参数组,和方法所在类的实际对象,通过调用Method对象的invoke方法来执行私有方法,执行结果就是私有方法的返回值。
原文:http://blog.csdn.net/aspnet2002web/article/details/18817581