JAVA反射API以及常见用法

来源:互联网 发布:unity5.3.4f1破解mac 编辑:程序博客网 时间:2024/04/25 00:59

先让我吐槽一下新浪博客…这篇笔记本来是发到新浪的,昨晚写了一个小时,最后困得睁不开眼终于写完发出去了。结果发出去准备打开看看排版怎么样的时候,神奇地发现写了一个小时的文章只剩下四分之一了,其他内容不翼而飞……当时真的有种想砸电脑的冲动……


JAVA反射机制:

利用JAVA已有的API动态操作类文件,完成对类的操作。
相对于静态只能在编译过程中操作类。
运用反射API可以在运行过程中,完成对类的增删改查
以完成特定的需求。


常见的反射API功能:


一:动态加载一个类

方法:使用Class类的静态方法
Class.forName(className)
来动态地将类加载到方法区,以便于进行进一步的增删改查操作。
会抛出ClassNotFoundException没有找到此类
例:
Class<ArrayList> cls = Class.forName


上面一行代码,将ArrayList类加载到了方法区中,并返回了引用,我们在这里把它赋给Class文件:cls。
Class中可以指定泛型,这样在创建实例时,就会返回泛型对象而不是Object,避免了强转。



二:动态获取类的信息
信息指类中的字段,方法,构造器信息等。

方法:使用cls引用访问类中的信息。
Method[]cls.getDeclaredMethods()
Constructor[]cls.getDeclaredConstructors()
Field[] cls.getDeclaredFields()
上面三个方法,分别返回了类中的方法,构造器,字段。
返回值分别是相应的对象数组。
同样的,也可以返回类中信息的单个对象,方法名一般是上述方法去掉后面用来表示复数的"s"。
Methodcls.getDeclaredMethod(name, parameterTypes)
Constructor cls.getDeclaredConstructor(parameterTypes)
Fieldcls.getDeclaredField(name)
其中,传入参数是为了确定想要获得的具体是哪个信息。
有了信息对象或对象数组后,就可调用该对象或数组中的对象(getName(),getValue()等)
来动态的获取类中信息。
例:
Constructor[] cons = cls.getDeclaredConstructors();<span style="white-space:pre"></span>for(Constructor con : cons){System.out.println(c.getName()+"..."+Arrays.toString(c.getParameterTypes()));}



上面一段代码获取了cls类的所有构造器的名字和参数类型。运用类似的方法也可以获得类中其他信息,比如属性,字段等。
三:动态创建对象
#分为两种情况:
1.动态创建对象,调用无参构造器,较为简单。
2.动态创建对象,调用有参构造器,较为复杂。
#第一种情况:
方法:将类加载到方法区后,调用引用对象的newInstance()方法,即
Objectcls.newInstance()
该方法返回一个Object对象,此对象为cls类调用无参构造器后创建的对象。
newInstance方法抛出三种异常:
IllegalAccessException创建例的过程中被private阻塞
InstantiationException实例化异常,对象不能被实例化
SecurityException安全异常
例:
<span style="white-space:pre"></span>Object obj = cls.newInstance();
若是指定过了泛型,则返回泛型对象而非Object。创建对象后,即可对对象进行常规操作。

#第二种情况:
方法:将类加载到方法区后,分为两步
1.找到指定的构造器信息2.传入指定的参数运行
实现第一步的方法:
Constructor cls.getDeclaredConstructor(parameterTypes) 
其中,parameterTypes是一个Class数组,传入数据类型的.class形式。
实现第二步的方法:
Object cons.newInstance(params)
其中,param是一个Object数组,传入具体的参数。
例:
Constructor cons = cls.getDeclaredConstructor(new Class[]{String.class,int.class});Object obj = cons.newInstance(new Object[]{"ZhangSan",25});


obj为使用指定构造器和参数创建好的实例,同样的,如果指定过泛型,则返回泛型对象。
四:动态调用方法
在创建好类实例的基础上,在运行过程中动态调用实例中的方法。
方法:将类加载到方法区后,分为两步
1.调用引用对象的getDeclaredMethod()方法,得到想要调用的方法2.使用Method中的invoke()方法。
实现第一步的方法:
Method getDeclaredMethod(method, paramTypes)
其中,method为方法名,paramTypes为class数组,代表参数类型,因为复写方法的存在,必须两个参数才能确定一个方法。调用此方法后,返回一个Method对象。
实现第二步的方法:
ObjectMethod.invoke(obj,params)
其中,第一个参数为一个对象实例,第二个对象表示参数列表,表示将在此实例上以此参数列表执行此方法,返回一个执行过后的实例。
例:
Object obj1 = new Person("MaYun");Method m = cls.getDeclaredMethod(setName,new Class[]{String.class});Object obj2 = m.invoke(obj,new Object[]{"SiCong"});


obj2执行了obj一中的setName方法,将"MaYun"换成了"SiCong"。


练习

实现一个经典案例:动态调用一个类中所有test开头,无参数的方法(JUnit3的原理)。

分析:必须使用反射实现,原因如下:
1.类型不明
2.方法不明

 做法:
1.动态加载类
2.动态创建对象
3.找到全部的方法
- 遍历,检查哪个方法以test为开头
     而且方法参数类型的长度是0:找到以test为开头的无参数方法。
4.执行这个方法,传递null即可。 

代码(异常未作处理):
public static void JUnit3Demo(String className) throws Exception{Class cls = Class.forName(className);Method[] methods = cls.getMethods();Object obj = cls.newInstance(); //for(Method m : methods){if(m.getName().startsWith("test") && m.getParameterTypes().length==0){m.invoke(obj, null);}}}

0 0