还是从例开始,比如有一个类HelloWorld,它用方法sayHello()打印Hello World。
HelloWorld.java
view plaincopy to clipboardprint?
- package com.test;
- public class HelloWorld {
- public void sayHello() {
- System.out.println("Hello World");
- }
- }
package com.test;public class HelloWorld { public void sayHello() { System.out.println("Hello World"); }}
一般通过以下方法对其进行调用:
view plaincopy to clipboardprint?
- HelloWorld helloWorld = new HelloWorld();
- helloWorld.sayHello();
HelloWorld helloWorld = new HelloWorld(); helloWorld.sayHello();
但很多时候,我们需要用一种更灵活的方法去进行调用。比如,在配置文件里配置类的名称,然后在程序里自动调用。在调用方法的前后执行特定的操作等等。使用过JSF,Struts,或Spring的人对此一定不会陌生:JSF可以绑定页面元素与事件,Spring通过配置文件可以不必通过new语句而自动为对象等设置属性值等等。近年来,被广泛利用的DI,AOP等技术大都是利用了JAVA的动态属性。
下面我们对JAVA的动态属性做一个详细介绍。
首先,我们简单介绍一下JAVA语言里跟动态性有关的几个类和接口:
类或接口名 | 说明 |
---|---|
java.lang.Class | 它包含了类本身的结构信息(Constructor, Field, Method, Annotation等)与相应的操作方法 |
java.lang.reflect.AccessibleObject | Field, Method, Constructor等类的基类,主要提供权限控制机制 |
java.lang.reflect.Method | 包含方法的结构信息与方法操作 |
java.lang.reflect.Field | 包含属性的结构信息与属性操作 |
java.lang.reflect.Constructor | 包含构造方法的结构信息与构造方法的操作 |
java.lang.annotation.Annotation | 接口。附加在类,属性,方法等上的注释信息 |
java.lang.reflect.Proxy | 动态代理功能的一个很重要的类。可以通过它与InvocationHandler实现动态代理功能。 |
java.lang.reflect.InvocationHandler | 动态代理功能的一个很重要的接口。它与Proxy一起实现动态代理功能。 |
类结构信息的取得与实例的生成
可以通过几种方法取得Class信息:
通过对象引用:
this.getClass();
obj.getClass();
通过类名引用:
比如:HelloWorld.class
通过Class.forName(String className)取得
比如:Class.forName("com.test.HelloWorld");
取得Class信息后,就可以执行各种操作了。
view plaincopy to clipboardprint?
- try {
- Class helloWorldClass = Class.forName("com.test.HelloWorld");
- HelloWorld helloWorld = (HelloWorld)helloWorldClass.newInstance();
- helloWorld.sayHello();
- } catch (ClassNotFoundException e1) {
- //TODO
- } catch (InstantiationException e) {
- //TODO
- } catch (IllegalAccessException e) {
- //TODO
- }
try { Class helloWorldClass = Class.forName("com.test.HelloWorld"); HelloWorld helloWorld = (HelloWorld)helloWorldClass.newInstance(); helloWorld.sayHello(); } catch (ClassNotFoundException e1) { //TODO } catch (InstantiationException e) { //TODO } catch (IllegalAccessException e) { //TODO }
Class.forName方法可以根据指定的类名得到Class对象。Class.newInstance()方法可以为Class生成实例。它们有几种使用方法,具体的使用方法这里就不详述了。
方法:Method的取得与执行
可以通过Class.getMethod或Class.getMethods取得方法信息,还可以通过Class.invoke方法直接执行取得的方法。
- Method sayHelloMethod = HelloWorld.class.getMethod("sayHello", new Class[]{});
- sayHelloMethod.invoke(helloWorld, null);
Method sayHelloMethod = HelloWorld.class.getMethod("sayHello", new Class[]{}); sayHelloMethod.invoke(helloWorld, null);
Class.getMethod:根据方法名称和参数类型取得方法。第一个参数为方法名(String),第二个参数为方法的参数类型(Class []),无参数的时候,可以设置为null或者空Class[]。比如,某方法有2各参数,一个为String,一个为Integer,则第2各参数为new Class[]{String.class, Integer.class}。Class.getMethod只能取得public类型的Method,要取得所有类型的Method,可以用Class.getDeclaredMethod。
Method.invoke:执行方法。第一个参数为Method类型,第2个参数为需要传送给方法的参数值(Object[]),无参数的时候,可以设置为null。
Method.setAccessible:Method 父类AccessibleObject.setAccessible(boolean)可以设置方法等的访问权限。比如sayHelloMethod.invoke原则上只能执行非private方法,不过如果为private方法设置了setAccessible(true);同样可以执行此方法。
属性:Field的取得与属性值的设置与赋值(set/get)
Field跟Method基本上一样。可以通过Class.getField(fieldName),Class.getFields,Class.getDeclaredField,Class.getDeclaredFields等取得。同样可以通过Field.setAccessible方法重新设置访问权限。具体可以参考上面的Method部分的讲解,这里不做详述了。
上面介绍了利用Java的反射(reflect)功能存取Class,Method,Field等的基本方法。另外还可以取得附加在Class,Method,Field上的注释信息(Annotation)。