|
||||
我们先从一个类开始,现在随便写一个类
public class TestMain {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}}
首先,要明白什么是反射,你首先要明白java.lang.Class是什么
刚才那个文件TestMain是一个类,这个类保存在磁盘上的一个位置,名字叫做TestMain
。将其编译后,就生成一个TestMain.class的文件。
对于每个类而言,JRE都为其保留一个不变的Class 类型的对象。一个Class 对象包含
了特定某个类的有关信息。你可以通过调用Class 对象的有关方法,返回特定类的构造
器对象,方法对象和数据成员对象
先不用急着理解什么是Class,我们先取一个Class,以后慢慢再从实用中理解
取得一个Class类的方法有很多,最常用的有2种:
1、一种是用类名+.class取得。例如上面的TestMain类: Class clazz =
TestMain.class;等效与class.forName("clazz")
.class也可用在简单数据类型上,如int.class
2、另外一种是用对象的getClass方法:例如: TestMain tm = new TestMain
(); Class clazz = tm.getClass();
这里提2种高级获取Class类的方法,这2个方法对大多数人来说,可能不会用到。
第一种:读取一个class文件,可以得到一个byte[]数组,这个数组可以用
ClassLoader 类的defineClass方法,生成一个类。
第二种:这个方法是动态代理用到的,用法是: import
sun.misc.ProxyGenerator; ...
byte[] b = ProxyGenerator.generateProxyClass("proxy0", new Class[]{interf})
然后用返回得到的byte数组产生一个类。其中参数interf是一个接口。
其次说明一下Class.forName()这个方法,这个方法涉及到ClassLoader机制的原因,不
在这里讲了,讲起来比较复杂
Class.forName:你简单理解为用当前ClassLoader的find方法去找某个类。
首先要明白的就是在java中所有的类都会被表示成java.lang.Class类的实例,别
和.class文件弄混了,也别和类的概念弄混了
现在我们要开始用反射的机制对刚才那个类进行操作了。在我讲的过程中,大家先思考
一个问题:为什么要用反射调用刚才那个方法。
第一步,我们得到了一个Class对象:Class clazz = TestMain.class;
第二步,我们获取这个类的所有方法:
Method[] methods = clazz.getMethods();//java.lang.reflect.Method
第三步,我们要开始调用这些方法中的某一个了
import java.lang.reflect.*;
public class ReflectTest
{
public static void main(String[] args) {
Class clazz = TestMain.class;
Method[] methods = clazz.getMethods();
for(int i=0; i<methods.length; i++) {
String methodName = methods[i].getName();
System.out.println(methodName);
} } }
public class ReflectTest
{
public static void main(String args[]) throws Exception
{
Class clazz = TestMain.class;
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++)
{
String methodName = methods[i].getName();
System.out.println(methodName);
}
String[] param = new String[1];
param[0] = "my name";
Method m = clazz.getMethod("setName", new Class[] {
java.lang.String.class });
// 调用setName方法
Object o = clazz.newInstance();
m.invoke(o, param);
// 调用getName方法
m = clazz.getMethod("getName", null);
Object result = m.invoke(o);
System.out.println("result = " + result);
}
}
public class TestMain
{
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
最后那段代码:m.invoke(o, param);是调用你的method
反射机制在你所做的java应用中无所不在大家都在jsp中用过javaBean吧其实,刚才这
段代码我们再封装一次,就是javaBean的机制了.
javaBean的机制是这样的:
第一, 一个类有一个空的构造子
第二, 为这个类的所有私有成员提供了get和set方法(或其中之一)
私有成员和get,set方法遵循命名规则。即成员名第一个字母小写,get,set方法后面
带的私有成员名的第一个字母大写
为什么要有一个空的构造子?因为要用clazz.newInstance();生成这个对象
public class OneBean {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}}
现在要修改刚才这个试验了。这个的试验目的是:在不改变代码的情况下,调用配置的
方法为了方便,这里不用xml,但原理是一样的
好了,代码改好了:
public static void main(String args[]) throws Exception
{
String className = System.getProperty("className");
System.out.println("className = " + className);
Class clazz = Class.forName(className);
String[] param;
String temp = System.getProperty("sname");
if (temp == null)
param = null;
else
param = new String[] { temp };
String methodName = System.getProperty("methodName");
Method m = null;
if (temp == null)
m = clazz.getMethod(methodName, null);
else
m = clazz.getMethod(methodName, new Class[] {
java.lang.String.class });
// 调用方法
Object o = clazz.newInstance();
m.invoke(o, param);
}
这里的原理是从配置文件中指定需要调用那些函数和方法,并知名其参数。首先,设
置环境变量,设置的方法大家应该知道吧,就是指定java虚拟机的参数,-Dkey=value
这里key我设置为如下: 两块石头(69707208) 22:38:35 这里key我设置为如下: -
Dzz.test.className=zz.test.TestMain -Dzz.test.name="my name" -
Dzz.test.methodName="setN 大家一起乐(125197462) 22:38:41 这样容易让大家都用
系统属性吧,我觉得还是用一个单独的属性类比较好 两块石头(69707208) 22:38:51
-Dzz.test.className=zz.test.TestMain -Dzz.test.name="my name" -
Dzz.test.methodName="setName" 两块石头(69707208) 22:39:16 无论用什么,其实
目的只有一个,就是能方便的获取到
环境变量取起来相对方便,如果配置内容很少的话,建议还是用环境变量来做。 首先
设置环境变量,这一步在你的ide环境中怎么做,大家知道吧
如果有不知道的,现在赶快说出来,我看看有没有必要再教这个。
用eclipse的,在你运行的这个类上点右键,选run as->run,然后在上面的tab上选
argument,在VM arguments里输入下面这样的内容: -
Dzz.test.className=zz.test.TestMain -Dzz.test.name="my name" -
Dzz.test.methodName="setN
-Dzz.test.className=zz.test.TestMain -Dzz.test.name="my name" -
Dzz.test.methodName="setName"
这部分不属于反射的内容,只是顺便提一下
刚才那段代码,我解释一下
String className = System.getProperty("zz.test.className"); 这个就是取刚刚在环境变量里配置的值的内容
Class clazz = Class.forName(className);
这个是寻找你配置的那个类。取得其Class对象
其实,看到这里,大家应该明白一些了,反射最主要的目的,就是跟配置挂钩,做配置要求的内容。如果配的合适,可以做很多复杂和有用的事情。
例如,现在那个Bean不是你写的,而是客户的,你就不可能直接去调。而是需要客户自己去装配这也就是为什么j2ee有了部署员的原因。因为很多东西需要装配在一起才能起作用。刚才那个用环境变量来改变程序的方式,你也可以看作是注入。
aop框架则稍微复杂一些,你可以简单的理解为在反射之外,提供了一些其他事情。
aop框架说起来比较复杂,这里不说了,有兴趣,一会我们再讨论
环境变量除了类名,其他可以不用改
下面我们总结一下:其实反射的机制内容非常多,我这里只说了一下如何找Class类和如何调用方法。 反射机制是java平台下各种框架实现的核心技术任何一个框架,struts,hibernate,spring等,以及应用服务器,例如tomcat,
weblogic,等都大量用了反射机制及思想 其目的就是能够把我们写的程序纳入到容器中运行。
反射有一个复杂的衍生体叫做动态代理 反射也是你要掌握java的最基础功力。
没有这个功力,你学习其他框架及技术的时候都会觉得很吃力。
例如在web.xml中随时可以见到这样一句话: <servlet-class>classname</ servlet-name>
以及参数<init-param>value</init-param>等,这些都是反射所需要的
我建议大家,如果要想把java学的上一个层次,首先好好查一查如何使用反射机制。这样对你以后学习各种框架有很多好处。