java 反射
package test.java;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.net.URL;
/**
*
* @author jalo
* @printout
* 反射:通过反射的api 接口,去探索一个运行期间的class 的内部结构,并且根据内部结构,决定方法怎么样去调用。反射可以在运行期间动态的加载
一个类进来,动态的new 一个对象出来,动态去了解这个对象内部的结构,动态的去调用这个对象的某一个方法
好处是,配置文件里别的不用写,就写类的名字,就可以动态的把这个类加载进来。加载进来好处:spring / struts / hibernate 很多时候
类的名字是需要配置在配置文件里的,要用类的对象,就通过反射机制new 出来。以下介绍了反射的5个作用,通过反射,可以了解这个类的方方面面,很
类似于反汇编,但是反射机制还是看不到类的具体实现。
*
*/
public class TestReflection {
public static void main(String[] args) {
String ClassName = "test.java.TT";//类的名字,可以配置在properties 文件
try {
Class cs = Class.forName(ClassName);//用classLoader 把类load 进来,只是这个函数在Class 类里
Object obj = cs.newInstance(); //反射1:new 一个对象,但是只能用Object 接收
TT obj = (TT)cs.newInstance(); //这里可以用类TT 强转,而后通过obj 点的方式直接调用函数,但是TT 这个类名现在
System.out.println(obj.getS());<span style="font-family: Arial, Helvetica, sans-serif;">//根本不知道,所以这三行代码不能用,只能用下面的反射invoke 方式调用类TT 的函数</span>
obj.beReflectCalled("eee", "ggg");
System.out.print("new 出来的对象是否是类test.java.TT 的对象 ");
System.out.println(obj instanceof test.java.TT);//true
//反射2:动态知道这个加载进来的类的方法
for(Method method : cs.getMethods())//如上所述,这里Object类的对象obj 无法调用TT类自己的函数,所以只能用反射方式拿到方法,在去调用
{
System.out.println("加载进来的类的方法为: "+method.getName());
if(method.getName().equals("beReflectCalled")){
Class returnType = method.getReturnType();//反射3:得到方法的返回类型
System.out.println("beReflectCalled()返回类型为" + returnType);
Class [] methodParams = method.getParameterTypes();//反射4:能得到方法的参数
for(Class methodParam : methodParams)
System.out.println("beReflectCalled()参数类型为: "+methodParam.getName());//两个参数为 class java.lang.String \n class java.lang.String
//反射5: new出来的TT 类对象,调用beReflectCalled()函数。public Object invoke(Object obj,Object... args),
//invoke 是可变参数的方法,这个意思是后面这个参数,可以传0个或多个变量做参数
if(methodParams.length==2 && methodParams[0].getName().equals("java.lang.String")
&& methodParams[1].getName().equals("java.lang.String")){
System.out.print("参数验证通过,beReflectCalled()调用成功,调用结果为:");method.invoke(obj, "aaa","bbb");
}
}
}
//反射2.5 如果已知方法名,可以这种方式调用方法,Method isEvenMethod = compareFuncClass.getMethod("isEven", int.class)
//但是注意第二个参数是Class, 而int 等基本类型也可以直接点class 对象的方式调用class 属性
//反射6:动态知道这个加载进来的类的成员变量
for(Field field : cs.getFields())
System.out.println("类的成员变量为: " + field.getName()); // i , s
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class TT{
static {
System.out.println("forName() 一加载,就调用这个static 块");//forName() 一加载,就调用这个static 块
}
public TT(){
System.out.println("newInstance()一运行,就调用构造函数");//newInstance()一运行,就调用构造函数
}
public int i ;
public String s ;
public void m1(int i){
this.i = i;
}
public String getS(){
return s ;
}
public void beReflectCalled(String param,String param1){
System.out.println("be Reflect Called "+param+param1);
}
}
执行结果为:
forName() 一加载,就调用这个static 块
newInstance()一运行,就调用构造函数
new 出来的对象是否是类test.java.TT 的对象 true
加载进来的类的方法为: m1
加载进来的类的方法为: getS
加载进来的类的方法为: beReflectCalled
beReflectCalled()返回类型为void
beReflectCalled()参数类型为: java.lang.String
beReflectCalled()参数类型为: java.lang.String
参数验证通过,beReflectCalled()调用成功,调用结果为:be Reflect Called aaabbb
加载进来的类的方法为: wait
加载进来的类的方法为: wait
加载进来的类的方法为: wait
加载进来的类的方法为: equals
加载进来的类的方法为: toString
加载进来的类的方法为: hashCode
加载进来的类的方法为: getClass
加载进来的类的方法为: notify
加载进来的类的方法为: notifyAll
类的成员变量为: i
类的成员变量为: s
反射作用扩展:
代码模板如下:
接口 引用 = new 类( ); // 这个用反射机制使其做到可配置
类 . 方法( ); // 知道了接口名,就知道了方法名和参数,所以这个可以写死。
我一改类还要改第一行代码,如果第一行直接用反射把这个类用new 出来,类名写成可配置的(像上面main 函数中的前三句代码那样),这样如果每次修改需要new 的类,直接通过改配置,替换配置文件中的类名即可,下面的函数调用整个都不需要改。
这样使程序的可扩展性大大增强。参考:java 接口引用指向对象 / 工厂模式