Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3251954
  • 博文数量: 530
  • 博客积分: 13360
  • 博客等级: 上将
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-13 13:32
文章分类

全部博文(530)

文章存档

2017年(1)

2015年(2)

2013年(24)

2012年(20)

2011年(97)

2010年(240)

2009年(117)

2008年(12)

2007年(8)

2006年(9)

分类: Java

2010-08-16 15:34:24

1.反射机制概述
Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

Java 反射机制主要提供了以下功能:
     在运行时判断任意一个对象所属的类。
     在运行时构造任意一个类的对象。
     在运行时判断任意一个类所具有的成员变量和方法。
     在运行时调用任意一个对象的方法。

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
     Class类:代表一个类。
     Field 类:代表类的成员变量(成员变量也称为类的属性)。
     Method类:代表类的方法。
     Constructor 类:代表类的构造方法。
     Array类:提供了动态创建数组,以及访问数组的元素的静态方法。[1]

2.动态语言和动态性
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods 的所有信息,并可于运行时改变fields内容或调用methods。
 
动态语言大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。[1]

3.Class类
       Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
      Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
      虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
      基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
      每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
      一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。[2]

获得Class的对象有三种方法:
    1、调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。例如:
         MyObject x;
         Class c1 = x.getClass();
    2、使用Class类的中静态forName()方法获得与字符串对应的Class对象。例如:
         Class c2=Class.forName("MyObject");
         MyObject必须是接口或者类的名字。
    3、获取Class类型对象的第三个方法非常简单。如果T是一个Java类型,那么T.class就代表了匹配的类对象。例如
         Class cl1 = Manager.class;
         Class cl2 = int.class;
         Class cl3 = Double[].class;
    注意:Class对象实际上描述的只是类型,而这类型未必是类或者接口。例如上面的int.class是一个Class类型的对象。由于历史原因,数组类型的getName方法会返回奇怪的名字。

java.lang.Class类是Reflection API 中的核心类,它有以下方法
    getName():获得类的完整名字。
    getClassLoader():返回该类的类加载器。
    getComponentType():返回表示数组组件类型的 Class。
    newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
        x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。
    getSuperclass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
    isArray():判定此 Class 对象是否表示一个数组类。

    与属性相关函数
    getfield(string name):field
    getfields():field[]:获得某个类的所有的公共(public)的字段,包括父类。
    getdeclaredfield(string name):field
    getdeclaredfields():field[]:获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。

    与方法相关的函数
    getmethod(string name,class... parametertypes):method
    getmethods():method[]:获得类的public类型的方法,包括父类。
    getdeclaredmethods():method[]:获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
    getdeclaredmethod(string name,class ...parametertypes):method:获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。

    与构造函数相关函数
    getConstructors():获得类的public类型的构造方法。
    getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
    构造函数不能继承,因此你调用getconstructor也只能返回这个类型中定义的所有公有构造函数。

例1:getName()举例
        Class c = Class.forName("java.lang.String");
        String name = c.getName();
        System.out.println(name);
参见:TestClasForname.java

例2:获取类的属性举例[3]
    public class A {
        public String a1="a1";
        private String a2="a2";
    }
    public class B extends A {
        public String b1="b1";
        private String b2="b2";
    }
如果利用A的类型类调用getfields,那么会返回a1属性,代码如下:
            Class c = Class.forName("A");
            Field[] fieldNames = c.getFields();
            for(int i=0;i                System.out.println(fieldNames[i].toString());
结果:
           public java.lang.String A.a1

如果利用B的类型类调用getfields,那么会返回a1和b1属性,代码如下:
            Class c = Class.forName("B");
            Field[] fieldNames = c.getFields();
            for(int i=0;i                 System.out.println(fieldNames[i].toString());
结果:
           public java.lang.String B.b1
           public java.lang.String A.a1

根据属性名称获取Field
           c = Class.forName("A");
           Field f = c.getField("a1");  //返回public java.lang.String A.a1
           Field f = c.getField("a2");  //出错
           Field f = c.getDeclaredField("a2");  //返回private java.lang.String A.a2
参见:TestGetFields.java

例3:获取类的方法举例
public class A {
    public String a1="a1";
    private String a2="a2";
    public void methodA1(){
    }
    private void methodA2(){
    }
}
public class B extends A {
     public String b1="b1";
     private String b2="b2";
     public void methodB1(){
     }
     private void methodB2(){
     }
}
通过getDeclaredMethods(),获取当前类所有方法
            Class c = Class.forName("A");
            Method[] array_method = c.getDeclaredMethods();
            for(int i=0;i                System.out.println(array_method[i].toString());
结果
      public void A.methodA1()
      private void A.methodA2()

通过getMethods(),获取当前类所有方法(包括父类)
            c = Class.forName("A");
            array_method = c.getDeclaredMethods();
            for(int i=0;i                System.out.println(array_method[i].toString());
结果
      public void A.methodA1()
      public final void java.lang.Object.wait() throws java.lang.InterruptedException
      public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException

通过指定的函数名,获取方法(不带参数)
            c = Class.forName("B");
            Class[] argsClass = new Class[]{};  
            Method m = c.getMethod("methodB1", argsClass );
            或 Method m = c.getMethod("methodB1", new Class[]{});
            System.out.println(m.toString());
结果
      public void B.methodB1()

通过指定的函数名,获取方法(带基本类型参数)
            c = Class.forName("B");
            Class[] parameterTypes = new Class[] { int.class, int.class };
            Method myMethod = c.getMethod("add", parameterTypes);
            System.out.println(myMethod.toString());
结果
       public int B.add(int,int)

通过指定的函数名,获取方法(参数为数组)
           c = Class.forName("B");
            parameterTypes = new Class[] {int[].class};
            myMethod = c.getMethod("testArray", parameterTypes);
            System.out.println(myMethod.toString());
结果
       public int B.testArray(int[])
参见:TestGetMothed.java

例4:获取构造函数
         Class c = Class.forName("A");
         Constructor[] constructors = c.getConstructors(); 
         for(int i=0;i                System.out.println(constructors[i].toString());
结果
      public A()
      public A(java.lang.String)
参见:TestConstructor.java

4.Field类
     gettype():class:获得该属性的类型。
     getname():string:获得属性名称。
     isaccessible():boolean:判断该属性是否是可以访问的,通常私有和保护的类型都是不可以访问的。
     get(object obj):object:获得实例obj的属性值,如果这个属性是非公有的,这里会报IllegalAccessException。
     set(objectobj,objectvalue):设置该实例的属性值
     setAccessible(booleanflag):设置该属性是否可以访问,如果你调用get和set方法,那么有可能会引发访问权限的错误,这个时候你可以调用setaccessible方法使得该属性可以访问。

例1:将private属性a2设置为可访问,并修改其值
        A a=new A();
        Field f=A.class.getDeclaredField("a2");
        f.setAccessible(true);
        System.out.println(f.get(a));
       
        f.set(a,"123456");
        System.out.println(f.get(a));
结果
        a2
        123456
参见:TestField.java

5.Method类

      getname():string:获得方法的名字。
      getreturntype():class:获得方法的返回值类型。
      getparametertypes():class[]:获得方法的参数类型。
      isaccessible():boolean:判断该方法是否是可以访问的。
      setaccessible(booleanflag):设置该方法是否可以访问。
      getexceptiontypes():class[]:获得该方法可能抛出的异常类类型。
      invoke(objectobj,object...args):object:调用实例obj的相应方法,其参数由args给定,如果没有参数那么可以什么都不写。

例1:通过invoke调用指定的函数
        public int add(int param1, int param2)
        {
             return param1 + param2;
        }
        Method addMethod = classType.getMethod("add", new Class[] { int.class, int.class });
        Object result = addMethod.invoke(invokeTester, new Object[] { new Integer(100), new Integer(200) });
        System.out.println((Integer) result);
参见:InvokeTester.java

总结
1.项目参见:ReflactionProject_20100816.zip
2.开发环境:myeclipse7.5 ,jdk1.6

参考文献
1.Java 反射机制深入研究. http://lavasoft.blog.51cto.com/62575/43218
2.深入研究java.lang.Class类 . http://lavasoft.blog.51cto.com/62575/15433
3.
4.Java反射机制总结(实例分析).http://shiyangxt.javaeye.com/blog/293022
阅读(2685) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~