JAVA反射机制

摘自于:http://hi.baidu.com/daniel_tu/blog/item/d2f2aa8fcab68ce9f11f3671.html

我们都知道,在 Java 中对象的实例是通过 new 关键字来创建的。不过,这种方法属于硬编码,不易改变,不灵活。比如在程序中硬编码创建的 Employee 对象,若要使用 Manager 来代替它,只能修改代码。Java 的反射机制是使其具有动态特性的非常关键的一种机制,也是在JavaBean 中广泛应用的一种特性。它可以让程序在运行期间加载编译期间不得而知的Class。并可以生成其实例、调用其方法、为其属性赋值。比如著名的 Hibernate 框架中就大量的使用了反射。本文通过简单的例子,简单介绍如何利用反射生成对象实例。

  1、利用反射创建对象实例。

1.        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

  上面的代码是否很熟悉?我们经常使用的 JDBC 驱动加载就是使用反射完成的。以上代码仅加载了对象到内存中,若要得到对象的实例只需调用 Class newInstance 方法即可。

  2、创建构造中带有参数的对象实例。

  Java 允许一个类拥有多个构造方法,并且可以在构造对象时传入参数用来初始化对象。比如 Employee 对象的构造方法需要一个 String 的参数,用来告诉其 id

1.        public class Employee {

2.            private String id;

3.            public Employee(String id) {

4.                this.id = id;

5.             }

6.        }

  在利用反射创建 Emplyee 对象时,我们必需为其创建 Constructor 对象,用来反映此 Class 对象所表示的类或接口的指定构造方法。

1.        // 生成class

2.        Class cls = Class.forName(className);

3.        // 参数类型

4.        Class[] types = new Class[] { String.class };

5.        // 类数值对象

6.        Object[] values = new Object[] { "001" };

7.        // 找到指定的构造方法

8.        Constructor constructor = cls.getDeclaredConstructor(types);

9.        // 设置安全检查,访问私有构造函数

10.     constructor.setAccessible(true);

7.        // 创建对象

11.     Employee e = (Employee) constructor.newInstance(values);

利用以上方法,我们就可以在运行期间加载创建对象实例,程序的灵活性也随之大大提高。

1.        import java.lang.reflect.*;

2.         

3.        public class Employee {

4.           private String id;

5.           public Employee(String id) {

6.                    this.id = id;

7.                    System.out.println(this.id);

8.           }

9.           /*

10.         * AccessibleObject 类是 FieldMethod Constructor 对象的基类。

11.         * 它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

12.         * 对于公共成员、默认(打包)访问成员、受保护成员和私有成员,

13.         * 在分别使用 FieldMethod Constructor 对象来设置或获取字段、调用方法,

14.         * 或者创建和初始化类的新实例的时候,会执行访问检查。

15.         * 在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java

16.         * Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。

17.         */

18.        public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {

19.                 Class c = Class.forName("Employee");

20.                 Class[] types = new Class[] {String.class};

21.                 Object[] values = new Object[] {"001"};

22.                 Constructor constructor = c.getDeclaredConstructor(types);

23.                 constructor.setAccessible(true);

24.                 Employee e = (Employee)constructor.newInstance(values);

25.        }

26.     }   

       目前流行的 PerlPythonRuby 等都是动态语言,所谓动态是指在程序运行期间可以改变程序结构和变量类型。Java 并不具备这一特点,所以Java不是动态语言。不过,尽管 Java 不是动态语言,但利用它的 Reflection(反射)机制同样可以在程序运行期间得到 Class 的名称,构造并生成其实例。利用 Reflection 我们可以为 fields(属性)赋值,调用其 method

  Class 对象的 getMethods 方法可以返回该 Class 表示的类(包括父类或接口)所声明的所有公共方法的 Method 数组。getMethod(String name, Class… parameterTypes),该方法返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String,用于指定方法名。parameterTypes 参数是按声明顺序标识该方法参数类型的 Class 对象的一个数组。如果 parameterTypes null,则按空数组处理。

1.        import java.lang.reflect.Method;

2.        public class User {

3.            private String id;

4.            public String name;

5.            public void setId(String id) {

6.                this.id = id;

7.             }

8.            public String getId() {

9.                return this.id;

10.          }

11.         public static void main(String[] args) {

12.                  Method[] methods = User.class.getMethods();       

13.                 for (Method method : methods) {

14.                      System.out.println(method.getName());

15.                  }

16.          }

17.     }

  运行上面的程序,控制台 User 及父类 Object 中的所有方法名称:
setId
main
getId
hashCode
getClass
wait
wait
wait
equals
toString
notify
notifyAll

  若想要取得某一方法,将 main 方法改为如下:

1.        public static void main(String[] args) {

2.            try {

3.              Method me = User.class.getMethod("getId", new Class [] {});

4.              System.out.println(me.getName());

5.        } catch (Exception ex) {

6.             ex.printStackTrace();

7.        }

8.        }

  若取得的方法需要参数,只要将参数类型传入即可:

1.        Method me = User.class.getMethod("setId", new Class [] {String.class});

  很多情况下,方法的参数类型是一个接口,或者是一个类型的父类型,这时若我们传入的参数类型为其子类型时将抛出 NoSuchMethodException 异常。这也很容易理解,Java 允许方法重载,相同名字的方法有可能会有多个,而区分这些方法的条件就是其参数个数和类型。所以如果不使用方法参数的实际类型做为查找条件,取到的方法很有可能是错误的。如下:

1.        import java.lang.reflect.Method;

2.        public class Manager extends User {

3.            private User u;

4.            public void set(User u) {

5.                this.u = u;

6.             }

7.            public static void main(String[] args) {

8.                try {

9.                     Method me = Manager.class.getMethod("set", new Class [] {Manager.class});

10.                  System.out.println(me.getName());

11.              } catch (Exception ex) {

12.                  ex.printStackTrace();

13.              }

14.          }

15.     }

  Manager 继承了 User,并且声明了一个 User 为参数的 set 方法,这时如果我们的 getMethod 的参数中使用 Manager 做为参数类型,将抛出异常。
java.lang.NoSuchMethodException: Manager.set(Manager)
    at java.lang.Class.getMethod(Class.java:1605)
    at Manager.main(Manager.java:12)

  这时,我们可以改用 User.class Manager.class.getSuperclass() 作为参数。

  若方法的参数类型为接口的场合,我们可以利用 Class getInterfaces 方法取得其实现的接口类型。Java 允许实现多个接口,所以 getInterfaces 方法返回一个 Class 的数组。

       下面讲述利用反射(reflect)机制在运行期间动态为Field 赋值。

  1、在生成对象后,直接访问属性。

1.        import java.lang.reflect.Field;

2.        public class TestField {

3.            public String test;

4.            public static void main(String[] args) {

5.                try {

6.                     Class cls = Class.forName("TestField");

7.                     Field f = cls.getField("test");

8.                     TestField tf = (TestField)cls.newInstance();

9.                     f.set(tf, "abc");

10.                  System.out.println(tf.test);

11.              } catch (Exception ex) {

12.                  System.out.println(ex);  

13.              }

14.          }

15.     }

  为了访问 Class TestField 的属性 test,我们需要生成其 Class 对象,利用 Class getField 方法得到 Field 后,只要调用 Field set 方法就可以为其赋值了。一般情况下,对象的属性都会设计为 private 的,这时,上面的程序就会抛出 java.lang.NoSuchFieldException: test 异常。其实,利用反射是可以访问 private 属性的,只要将上面的 getField 改为 getDeclaredField 方法即可。getDeclaredField()返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

 

你可能感兴趣的:(JAVA反射机制)