Java反射机制的一点思考
今天看到了有关Java运行时方面的一个方法getSomeValue(String propertyName,Object obj),大体上功能是根据一个对象的字段名称和该对象去获得该字段实际运行时的值。里面除了一些逻辑操作外,主要是用到了java.lang.reflect.Method类的invoke(Object obj, Object... args)方法,看了一下jdk的说明,还是一头雾水,决定自己写个小例子研究一下~
首先简单介绍一下Java的运行时机制,程序运行时,java运行时系统一直对所有的对象进行所谓的运行时类型识别(RTTI),以确定每个对象所属的类。JVM通常使用运行时类型信息来选择正确的方法去执行 。
*在java中,保存运行时类型信息的类叫Class类。
*对于每一个被装载的类型(不管是类还是接口), JVM都会相应地为它创建一个Class对象。在你的应用程序中,你可以得到并使用指向Class对象的引用。
*在运行期,一旦我们想生成一个类对象,JVM首先就会检查那个类型的Class对象是否已经载入。若尚未载入,JVM就会查找同名的.class文件,并将其载入。所以Java程序启动时并不是完全载入的,这一点与许多传统语言都不同。 一旦那个类型的Class对象进入内存,就用它创建那一类型的所有对象。
我们通常会用getClass(),Class.forName(),obj.class等等方法来操作运行时类,还有一个机制就是反射了,下面来看
如果不知道一个对象的准确类型,RTTI会帮助我们调查。但却有一个限制:类型必须是在编译期间已知的。
而反射使我们能在运行期间探察一个类,RTTI和“反射”之间唯一的区别就是:对RTTI来说,编译器会在编译期打开和检查.class文件。但对“反射”来说,.class文件在编译期间是不可使用的,而是由运行时环境打开和检查 ,我们利用反射机制一般是使用java.lang.reflect包提供给我们的类和方法
开始转向正题,写一个简单的例子来学习掌握Invoke方法
[code][br]1 public class Player [br]2 { [br]3 protected Object getSomeValue(String propertyName,Object obj) throws NoSuchMethodException [br]4 { [br]5 Object rtnVal=null; [br]6 try [br]7 { [br]8 Method method=obj.getClass().getMethod("get"+Character.toUpperCase(propertyName.charAt(0))+propertyName.substring(1) ); [br]9 rtnVal=method.invoke(obj); [br]10 } [br]11 catch(NoSuchMethodException e) [br]12 { [br]13 throw e; [br]14 } [br]15 catch(Exception ex) [br]16 { [br]17 ex.printStackTrace(); [br]18 } [br]19 return rtnVal; [br]20 } [br]21 }[/code]
方法的2个参数,一个是字段的名称,第二个就是你要获得对应字段值的运行时对象了,首先定义了1个Object类型返回值rtnVal,然后通过getClass(),getMethod()方法获得对应字段的get方法,括号里面的字符串操作就是是为了这个,这时,关键的来了,我们调用了刚才获得method对象的invoke(obj)方法,obj是你需要定位的运行时对象,后面Object... args省略了,因为get字段的方法不带参数嘛,所以这样写
接着我们从Player类派生出一个类Runner来测试上面的方法
[code][br]1 class Runner extends Player [br]2 { [br]3 private String playingType="400m"; [br]4 [br]5 [br]6 public String getPlayingType() { [br]7 return playingType; [br]8 } [br]9 [br]10 [br]11 public void runnerTest(Object obj) throws NoSuchMethodException [br]12 { [br]13 System.out.println(getSomeValue("playingType", obj)); [br]14 } [br]15 }[/code]
我们设置了1个字段表示跑步运动员参加的比赛项目,然后相应生成了get方法,然后有个测试方法,输出字段的值,补充一点,上面的getSomeValue方法我们让他抛出了NoSuchMethod异常,所以在主方法调用时注意捕获
接下来可以在Player类中写个主方法来测试
[code][br]1 public static void main(String[] args) [br]2 { [br]3 Runner r=new Runner(); [br]4 try [br]5 { [br]6 r.runnerTest(r); [br]7 } [br]8 catch(Exception e) [br]9 { [br]10 e.printStackTrace(); [br]11 } [br]12 }[/code]
r是我们运行时创建的对象,然后运行测试的方法中也传入了运行时对象r,输出了对应字段的值"400m",当然,为了证实这一点,我们也可以为Runner类写个带playingType字段的构造函数来运行时改变它的值,输出也会是你当前创建对象的实时值
小结一下,invoke(Object obj, Object... args)这个方法就是调用此Method
对象表示的底层方法,
如果底层方法是静态的,那么可以忽略指定的 obj
参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args
数组长度可以为 0 或 null。
如果底层方法是实例方法,则使用动态方法查找来调用它,
如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。