Java,本身是一门非常优秀的面向对象语言,严格的面向对象语法,跨平台,学习简单…使用相当广泛,它的优秀是无可厚非的,但它也有一些争议,比如class文件的半编译方式会降底系统性能,还有单继承的限制有时候让人不爽(可以理解,但这点我支持java的方式),还有最重要的就是Java导致很多开发人员只会编代码,而对很多计算机科学应该知道的知识不了解.因为Java太上层了.但如果一个java出生的程序员又对计算机科学有广泛的爱好,那java可以带来很多面向对象编程,架构等等的思路指导.而且现在很多的脚本语言都是趋近于java的语法,如果你能理解Java的精髓,那么Javascript,Flash的ActionScript,甚至PHP对你来说都很简单(我就是这样).当然相关方面应用的知识是一定要知道的~程序语言只是工具而已!!
Java的应用很广泛,桌面程序,服务器应用,手持设备,其它设备基本上都支持由java来驱动.我们也可以扩展Java虚拟机(JNI,或源代码级修改)(哈哈,这是我最喜欢做的事了),使自己的Java虚拟机可以提供自己的包给上层开发人员调用,这些包可能直接操作设备,也可能是像支付功能一样的那些”只能让少数重要的人员维护”的功能.
吹了这么多废话,说说Java的反射机制吧,通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们.
给大家一些反射机制应用的举例:
- 分析类文件,如果你要做Java的IDE,又不想分析源代码,或许用反射机制,可以取得任意一个类的所有基本信息.eclipse可能就是这样实现的.(我说可能,是因为eclipse要分析C或其它语言的时候,还是得分析源代码)
- 脚本引擎,怎么样让一串字符串命令执行起来?这就要靠反射机制去动态创建类,动态执行里面的函数,所以,它甚至可以拿来远程注入代码,我怕教坏了你们这群孩子,就不明确地说它了.
- 访问一些不能访问的变量或属性,做破解也挺有用.但自己开发个人觉得这样的功能完全不要去用它,不然你的代码就是火星文,而你,也就成了火星人.所以本文里我不会过多讲它怎么越界访问变量.
上面这些都是我能想到的.当然实际还可以做很多很多的事.
Class类
java反射的核心类包括Class,Method,Field等,Class<T>这个类,用过C++的STL的人都熟悉,它代表一个类型.Java里通过它,可以取得这个类的所有信息,包括类的成员,函数,构造函数等信息.我们可以用Class.forName方法通过字符串或得这个类型,如:Class<String>可以通过 Class.forName(“java.lang.String”)取得.也可以通过一个对象,去取得它的Class,如
Object a=new Object();
Class c=a.getClass();//取得一个对象所关联的Class.
有些基本类型,比如int,它不属于什么类(严格地说,是属于java.lang.Number类),所以一般都用int.class直接取得.取得这一个Class对象了,就可以用它的newInstance 创建这个类的实例. 如下面的语句:
Object str=Class.forName(“java.lang.String”).newInstance();
相当于
Object str=new String();
可能很多人看不出上面使用反射机制生成对象有什么优势,下面的写法多简单啊.但请注意,下面的语句是在编代码的时候写死的.而上面语句,如果java.lang.String这个参数是其它字符串,那创建的就是其它类的实例.这个字符串可以来自于磁盘,来自于网络,或来自于用户输入.所以,它就是活的.
当然,上面的问题在于,你只能使用无参数的构造函数,这样肯定不行,所以,需要用Class去取得Constructor对象,再调用它的newInstance函数,就可以通过任意构造函数创建对象了.取得Constructor对象的方法和后面取得Method的方法差不多.
有一点一定不要迷糊:
1.类和对象的概念
2.Class类,和Class类的对象的概念.
概念2里,Class是一个类,Class类的对象是一个对象.它是完全附合概念1的,只不过Class类的对象是封装一个类的信息罢了!这点千万别迷糊!!!
Field类
它就是类成员变量的对象. Field有一系列的set函数,可以为指定对象的Field设定值.一个Field可以通过Class的getDeclaredField获得.然后使用它的setXXX函数去为某个对象的Field设置值.
Method类
它就是类的成员函数对象.可以使用Class类的getDeclaredMethod取得.这个函数需要指明你想要取得的函数对象的参数信息,因为函数可以重载.
例如,如果类A里面声明了两个函数
public int func(int a,int b);
public int func(String a,String b);
那要取得第一个函数对象,就
A a=new A();
Method m1=a.getClass().getDeclaredMethod(“func”,int.class,int.class);
Method m2=a.getClass().getDeclaredMethod(“func”,Class.forName(“java.lang.String”),Class.forName(“java.lang.String”));
取得了Method对象后,可以使用这个Method对象的invoke函数去调用这个函数.
AccessibleObject类
它是那些可访问类的父类,提供类的成员的访问权限信息,也可以运行时修改它的访问权限:isAccessible/setAccessible 查询和设置.对于私有变量/函数,需要先setAccessible为true,然后才能访问.非常规情况下(如破解)时,可以用到.
下面举一个Java反射机制的简单例子程序来结束这篇文章.下一篇文章还没写,估计要一两周才出来吧!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
//ReflectTest.java
package
com.hoverlees.reflect;
import
java.lang.reflect.Field;
public
class
ReflectTest {
public
int
intVal=
100
;
public
String strVal=
"hoverlees"
;
private
int
pVal=
200
;
public
ReflectTest(){
}
public
ReflectTest(
int
a,String b,
int
c){
intVal=a;
strVal=b;
pVal=c;
}
public
void
func(
int
a,
int
b){
System.out.println(
"public func(int a,int b) invoked. The sum is:"
+(a+b));
}
public
void
func(String a,String b){
System.out.println(
"public func(String a,String b) invoked. The sum is:"
+(a+b));
}
private
void
pfunc(String a,String b){
System.out.println(
"private pfunc(int a,int b) invoked. The sum is:"
+(a+b));
}
public
int
getPrivateValue(){
return
pVal;
}
}
//Main.java
package
com.hoverlees.reflect;
import
java.lang.reflect.Constructor;
import
java.lang.reflect.Field;
import
java.lang.reflect.Method;
public
class
Main {
//不要把main写在ReflectTest类里,不然它本来就可以直接访问ReflectTest里的私有属性.
public
static
void
main(String[] args){
ReflectTest obj=
new
ReflectTest();
Class c=
null
;
c=obj.getClass();
/*
//或者使用这种方法取得Class.
try {
c = Class.forName("com.hoverlees.reflect.ReflectTest");
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
return;
}
*/
try
{
//访问正常属性
Field intf=c.getDeclaredField(
"intVal"
);
intf.setInt(obj,
8766
);
// obj.intVal=8766;
Field strf=c.getDeclaredField(
"strVal"
);
strf.set(obj,
"lees"
);
//obj.strVal="lees";
//强制访问obj.pVal,正常情况下是不可访问的.注:此方法违背面向对象思想,正常情况不推荐使用
Field privatef=c.getDeclaredField(
"pVal"
);
privatef.setAccessible(
true
);
privatef.setInt(obj,
300
);
//obj.pVal=300;
//访问函数
Method m1=c.getDeclaredMethod(
"func"
,
int
.
class
,
int
.
class
);
m1.invoke(obj,
10
,
20
);
//obj.func(10,20);
//下面用了Class.forName和object.getClass(),只是为了再次强调它们的效果是一样的.在不同的场合下,用不同的方法.
Method m2=c.getDeclaredMethod(
"func"
, Class.forName(
"java.lang.String"
),
new
String().getClass());
m2.invoke(obj,
"hover"
,
"lees"
);
//强制问题私有函数,这儿举例的 "".getClass() 还是相当于new String().getClass().
Method m3=c.getDeclaredMethod(
"pfunc"
,
""
.getClass(),Class.forName(
"java.lang.String"
));
m3.setAccessible(
true
);
m3.invoke(obj,
"hover"
,
"lees"
);
//再举个构造函数的例子
Constructor ct=c.getConstructor(
int
.
class
,
""
.getClass(),
int
.
class
);
ReflectTest obj2=(ReflectTest)ct.newInstance(
1
,
"obj2"
,
2
);
System.out.println(
"obj2.intVal="
+obj2.intVal+
";obj2.strVal="
+obj2.strVal+
";obj2.pVal="
+obj2.getPrivateValue());
}
catch
(Exception e) {
e.printStackTrace();
}
System.out.println(
"obj.intVal="
+obj.intVal+
";obj.strVal="
+obj.strVal+
";obj.pVal="
+obj.getPrivateValue());
}
}
|