Java反射机制及应用(一)

很久没有写点文章了,最近工作很忙,私事很多.

最近在做android软件的架构,虽然,我自己并不喜欢编写java程序,但现在没有android 开发的人手,需要我先把底层的架构做好,边做边招人…

此连载打算写两篇,本文主要打算讨论Java的反射机制的基本原理和相关类,下一篇写反射机制的一个应用.

本文链接 http://www.hoverlees.com/blog/?p=513

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反射机制的简单例子程序来结束这篇文章.下一篇文章还没写,估计要一两周才出来吧!

//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());
	}
}

Leave a comment

Your email address will not be published. Required fields are marked *