搜档网
当前位置:搜档网 › java反射示例

java反射示例

一、什么是反射:
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射机制的语言。最近,反射机制也被应用到了视窗系统、操作系统和文件系统中。

反射本身并不是一个新概念,尽管计算机科学赋予了反射概念新的含义。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

二、什么是Java中的类反射:
Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法。Java 的这一能力在实际应用中用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。
Reflection 是 Java 被视为动态(或准动态)语言的关键,允许程序于执行期 Reflection APIs 取得任何已知名称之 class 的內部信息,包括 package、type parameters、superclass、implemented interfaces、inner classes, outer class, fields、constructors、methods、modifiers,並可于执行期生成instances、变更 fields 內容或唤起 methods。

三、Java类反射中所必须的类:
Java的类反射所需要的类并不多,它们分别是:Field、Constructor、Method、Class、Object,下面我将对这些类做一个简单的说明。
Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
Object类:每个类都使用 Obj

ect 作为超类。所有对象(包括数组)都实现这个类的方法。

四、Java的反射类能做什么:
看完上面的这么多我想你已经不耐烦了,你以为我在浪费你的时间,那么好吧!下面我们就用一些简单的小例子来说明它。
首先我们来看一下通过Java的反射机制我们能得到些什么。
首先我们来写一个类:

java 代码



1.import java.awt.event.ActionListener;
2.import java.awt.event.ActionEvent;
3.class A extends Object implements ActionListener{
4.private int a = 3;
5.public Integer b = new Integer(4);
6.public A(){}
7.public A(int id,String name){}
8.public int abc(int id,String name){return 0;}
9.public void actionPerformed(ActionEvent e){}
10.}

你可能被我这个类弄糊涂了,你看不出我要做什么,那就不要看这个类了,这个类是用来测试的,你知道知道它继承了Object类,有一个接口是ActionListener,两个属性int和Integer,两个构造方法和两个方法,这就足够了。
下面我们把A这个类作为一个反射类,来过去A类中的一些信息,首先我们先来过去一下反射类中的属性和属性值。

java 代码



1.import https://www.sodocs.net/doc/e97706014.html,ng.reflect.*;
2.class B{
3.public static void main(String args[]){
4.A r = new A();
5.Class temp = r.getClass();
6.try{
7.System.out.println("反射类中所有公有的属性");
8.Field[] fb =temp.getFields();
9.for(int j=0;j10.Class cl = fb[j].getType();
11.System.out.println("fb:"+cl);
12.}
13.
14.System.out.println("反射类中所有的属性");
15.Field[] fa = temp.getDeclaredFields();
16.for(int j=0;j17.Class cl = fa[j].getType();
18.System.out.println("fa:"+cl);
19.}
20.System.out.println("反射类中私有属性的值");
21.Field f = temp.getDeclaredField("a");
22.f.setAccessible(true);
23.Integer i = (Integer)f.get(r);
24.System.out.println(i);
25.}catch(Exception e){
26.e.printStackTrace();
27.}
28.}
29.
30.}

这里用到了两个方法,getFields()、getDeclaredFields(),它们分别是用来获取反射类中所有公有属性和反射类中所有的属性的方法。另外还有getField(String)和getDeclaredField(String)方法都是用来过去反射类中指定的属性的方法,要注意的是getField方法只能取到反射类中公有的属性,而getDeclaredField方法都能取到。
这里还用到了Field 类的setAccessible方法,它是用来设置是否有权限访问反射类中的私有属性的,只有设置为true时才可以访问,默认为false。另外 Field类还有set(Object AttributeName,Object value)方法,可以改变指定属性的值。

下面我们来看一下如何获取反射类中的构造方法

java 代码



1.import https://www.sodocs.net/doc/e97706014.html,ng.reflect.*;
2.public class SampleConstructor {
3.public static void main(String[]

args) {
4.A r = new A();
5.printConstructors(r);
6.}
7.
8.public static void printConstructors(A r) {
9.Class c = r.getClass();
10.//获取指定类的类名
11.String className = c.getName();
12.try {
13.//获取指定类的构造方法
14.Constructor[] theConstructors = c.getConstructors();
15.for(int i=0; i16.//获取指定构造方法的参数的集合
17.Class[] parameterTypes = theConstructors[i].getParameterTypes();
18.
19.System.out.print(className + "(");
20.
21.for(int j=0; j22.System.out.print(parameterTypes[j].getName() + " ");
23.
24.System.out.println(")");
25.
26.}
27.}catch(Exception e) {
28.e.printStackTrace();
29.}
30.}
31.}
这个例子很简单,只是用getConstructors()方法获取了反射类的构造方法的集合,并用Constructor类的getParameterTypes()获取该构造方法的参数。

下面我们再来获取一下反射类的父类(超类)和接口

java 代码



1.import java.io.*;
2.import https://www.sodocs.net/doc/e97706014.html,ng.reflect.*;
3.
4.public class SampleInterface {
5.public static void main(String[] args) throws Exception {
6.A raf = new A();
7.printInterfaceNames(raf);
8.}
9.
10.public static void printInterfaceNames(Object o) {
11.Class c = o.getClass();
12.//获取反射类的接口
13.Class[] theInterfaces = c.getInterfaces();
14.for(int i=0; i15.System.out.println(theInterfaces[i].getName());
16.//获取反射类的父类(超类)
17.Class theSuperclass = c.getSuperclass();
18.System.out.println(theSuperclass.getName());
19.}
20.}

这个例子也很简单,只是用Class类的getInterfaces()方法获取反射类的所有接口,由于接口可以有多个,所以它返回一个 Class数组。用getSuperclass()方法来获取反射类的父类(超类),由于一个类只能继承自一个类,所以它返回一个Class对象。

下面我们来获取一下反射类的方法

java 代码



1.import https://www.sodocs.net/doc/e97706014.html,ng.reflect.*;
2.public class SampleMethod {
3.
4.public static void main(String[] args) {
5.A p = new A();
6.printMethods(p);
7.}
8.
9.public static void printMethods(Object o) {
10.Class c = o.getClass();
11.String className = c.getName();
12.Method[] m = c.getMethods();
13.for(int i=0; i14.//输出方法的返回类型
15.System.out.print(m[i].getReturnType().getName());
16.//输出方法名
17.System.out.print(" "+m[i].getName()+"(");
18.//获取方法的参数
19.Class[] parameterTypes = m[i].getParameterTypes();
20.for(int j=0; j21.System.out.print(parameterTypes[j].getName());
22.if(parameterTypes.length>j+1){
23.System.out.print(",");
24.}
25.}
26.
27.System.out.println(")");
28.}
29.
30.}
31.
32.}


个例子并不难,它只是获得了反射类的所有方法,包括继承自它父类的方法。然后获取方法的返回类型、方法名和方法参数。

接下来让我们回过头来想一想,我们获取了反射类的属性、构造方法、父类、接口和方法,可这些东西能帮我们做些什么呢!!
下面我写一个比较完整的小例子,来说明Java的反射类能做些什么吧!!

java 代码



1.import https://www.sodocs.net/doc/e97706014.html,ng.reflect.Constructor;
2.import https://www.sodocs.net/doc/e97706014.html,ng.reflect.Method;
3.
4.public class LoadMethod {
5.public Object Load(String cName,String MethodName,String[] type,String[] param){
6.Object retobj = null;
7.try {
8.//加载指定的Java类
9.Class cls = Class.forName(cName);
10.
11.//获取指定对象的实例
12.Constructor ct = cls.getConstructor(null);
13.Object obj = ct.newInstance(null);
14.
15.//构建方法参数的数据类型
16.Class partypes[] = this.getMethodClass(type);
17.
18.//在指定类中获取指定的方法
19.Method meth = cls.getMethod(MethodName, partypes);
20.
21.//构建方法的参数值
22.Object arglist[] = this.getMethodObject(type,param);
23.
24.//调用指定的方法并获取返回值为Object类型
25.retobj= meth.invoke(obj, arglist);
26.
27.}
28.catch (Throwable e) {
29.System.err.println(e);
30.}
31.return retobj;
32.}
33.
34.//获取参数类型Class[]的方法
35.public Class[] getMethodClass(String[] type){
36.Class[] cs = new Class[type.length];
37.for (int i = 0; i < cs.length; i++) {
38.if(!type[i].trim().equals("")||type[i]!=null){
39.if(type[i].equals("int")||type[i].equals("Integer")){
40.cs[i]=Integer.TYPE;
41.}else if(type[i].equals("float")||type[i].equals("Float")){
42.cs[i]=Float.TYPE;
43.}else if(type[i].equals("double")||type[i].equals("Double")){
44.cs[i]=Double.TYPE;
45.}else if(type[i].equals("boolean")||type[i].equals("Boolean")){
46.cs[i]=Boolean.TYPE;
47.}else{
48.cs[i]=String.class;
49.}
50.}
51.}
52.return cs;
53.}
54.
55.//获取参数Object[]的方法
56.public Object[] getMethodObject(String[] type,String[] param){
57.Object[] obj = new Object[param.length];
58.for (int i = 0; i < obj.length; i++) {
59.if(!param[i].trim().equals("")||param[i]!=null){
60.if(type[i].equals("int")||type[i].equals("Integer")){
61.obj[i]= new Integer(param[i]);
62.}else if(type[i].equals("float")||type[i].equals("Float")){
63.obj[i]= new Float(param[i]);
64.}else if(type[i].equals("double")||type[i].equals("Double")){
65.obj[i]= new Double(param[i]);
66.}else if(type[i].equals("boolean")||type[i].equals("Boolean")){
67.obj[i]=new Boolean(param[i]);
68.}else{
69.obj[i] = param[i];
70.}
71.}
72.}
73.return obj;
74.}
75.}

这是我在工作中写的一个实现Java在运行时加载指定的类,并调用指定方法的一个

小例子。这里没有main方法,你可以自己写一个。
Load方法接收的五个参数分别是,Java的类名,方法名,参数的类型和参数的值。

结束语:
Java语言反射提供一种动态链接程序组件的多功能方法。它允许程序创建和控制任何类的对象,无需提前硬编码目标类。这些特性使得反射特别适用于创建以非常普通的方式与对象协作的库。Java reflection 非常有用,它使类和数据结构能按名称动态检索相关信息,并允许在运行着的程序中操作这些信息。Java 的这一特性非常强大,并且是其它一些常用语言,如 C、C++、Fortran 或者 Pascal 等都不具备的。

但反射有两个缺点。第一个是性能问题。用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。即使测试中最坏情况下的计时图显示的反射操作只耗用几微秒。仅反射在性能关键的应用的核心逻辑中使用时性能问题才变得至关重要。














被测试类

TestMethod.java



package cn.reflect;

public class TestMethod {
public void test() {
System.out.println("---------");
}

public String meth() {
return "hello";
}

public static String meo() {
return "wsm";
}
public String tests(String s){
System.out.println("++++++++++");
return s;
}
}



测试类

package cn.reflect;

import https://www.sodocs.net/doc/e97706014.html,ng.reflect.InvocationTargetException;
import https://www.sodocs.net/doc/e97706014.html,ng.reflect.Method;

public class TestReflection {
public static void main(String[] args) throws ClassNotFoundException,
SecurityException, NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException, InstantiationException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class c = null;
c = cl.loadClass("cn.reflect.TestMethod");
Method[] m = c.getMethods();
//第一个参数是方法名 第二个参数是tests方法形式参数的类型,对于多个形式参数,会有多个类型,他们共同组成一个Class[]数组,对于tests方法,只有一个参数
Method method = c.getMethod("tests", new Class[]{String.class});
// for (int i = 0; i < m.length; i++) {
// // 输出方法的返回类型
// System.out.print(m[i].getReturnType().getName());
// // 输出方法名
// System.out.print(" " + m[i].getName() + "(");
// // 获取方法的参数
// Class[] parameterTypes = m[i].getParameterTypes();
// for (int j = 0; j < parameterTypes.length; j++) {
// System.out.print(parameterTypes[j].getName());
// if (parameterTypes.length > j + 1) {
// System.out.print(",");
// }
// }
//
// System.out.println(")");
// }
Object o = null;
//第一个参数是TestMethod类的

实例对象 第二个参数是tests方法的参数,参数可能有多个,他们共同组成一个Object数组
o = method.invoke(c.newInstance(), new Object[] {new String("asjdflkajglkjadsflk")});
System.out.println(o);
}
}




另:附Class.getMethod方法说明



getMethod
public Method getMethod(String name,
Class... parameterTypes)
throws NoSuchMethodException,
SecurityException 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形参类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。
如果 name 是 ";" 或 "",则将引发 NoSuchMethodException。否则,要反映的方法由下面的算法确定(设 C 为此对象所表示的类):
1.在 C 中搜索任一匹配的方法。如果找不到匹配的方法,则将在 C 的超类上递归调用第 1 步算法。
2.如果在第 1 步中没有找到任何方法,则在 C 的超接口中搜索匹配的方法。如果找到了这样的方法,则反映该方法。
在 C 类中查找匹配的方法:如果 C 正好声明了一个具有指定名称的公共方法并且恰恰有相同的形参类型,则它就是反映的方法。如果在 C 中找到了多个这样的方法,并且其中有一个方法的返回类型比其他方法的返回类型都特殊,则反映该方法;否则将从中任选一个方法。
注意,类中可以有多个匹配方法,因为尽管 Java 语言禁止类声明带有相同签名但不同返回类型的多个方法,但 Java 虚拟机并不禁止。这增加了虚拟机的灵活性,可以用来实现各种语言特性。例如,可以使用桥方法 (brige method)实现协变返回;桥方法以及将被重写的方法将具有相同的签名,不同的返回类型。

请参阅Java 语言规范 第 8.2 和 8.4 节。


参数:name - 方法名 parameterTypes - 参数列表 返回:与指定的 name 和 parameterTypes 匹配的 Method 对象 抛出:NoSuchMethodException - 如果找不到匹配的方法,或者方法名为 "" 或 "" NullPointerException - 如果 name 为 nullSecurityException - 如果存在安全管理器 s,并满足下列任一条件: ?调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝访问方法
?调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包
从以下版本开始:JDK1.1
还有Method.invoke方法说明



invoke
public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException 对带有指定

参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。

如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。

如果底层方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。

如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。

如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。


参数:obj - 从中调用底层方法的对象 args - 用于方法调用的参数 返回:使用参数 args 在 obj 上指派该对象所表示方法的结果 抛出:IllegalAccessException - 如果此 Method 对象强制执行 Java 语言访问控制,并且底层方法是不可访问的。 IllegalArgumentException - 如果该方法是实例方法,且指定对象参数不是声明底层方法的类或接口(或其中的子类或实现程序)的实例;如果实参和形参的数量不相同;如果基本参数的解包转换失败;如果在解包后,无法通过方法调用转换将参数值转换为相应的形参类型。 InvocationTargetException - 如果底层方法抛出异常。 NullPointerException - 如果指定对象为 null,且该方法是一个实例方法。 ExceptionInInitializerError - 如果由此方法引起的初始化失败。

相关主题