(转)Android反射机制实现与原理
来源:互联网 发布:.science域名 搜索引擎 编辑:程序博客网 时间:2024/06/06 02:37
一、反射的概念及在Java中的类反射
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。在计算机科学领域,反射是一类应用,它们能够自描述和自控制。这类应用通过某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
好,了解这些,那我们就知道了,我们可以利用反射机制在Java程序中,动态的去调用一些protected甚至是private的方法或类,这样可以很大程度上满足我们的一些比较特殊需求。你当然会问,反射机制在Android平台下有何用处呢?
我们在进行Android程序的开发时,为了方便调试程序,并快速定位程序的错误点,会从网上下载到对应版本的Android
那么,对于这个问题,第一种方法就是自己去掉Android源码中的"@hide"标记,然后重新编译生成一个SDK。另一种方法就是使用Java反射机制了,可以利用这种反射机制访问存在访问权限的方法或修改其域。
废话半天,该入正题了,在进入正题之前,先给上一个反射测试类的代码,该代码中定义了我们需要进行反射的类,该类并没有实际的用途,仅供做为测试类。提示:本文提供的代码,并不是Android平台下的代码,而是一个普通的Java程序,仅仅是对Java反射机制的Demo程序,所以大家不要放在Android下编译啊,否则出现问题,别追究我的责任啦!
importjava.awt.event.ActionEvent;
importjava.awt.event.ActionListener;
importjava.io.Serializable;
publicclassReflectionTest extends ObjectimplementsActionListener,Serializable{
//成员变量
privateintbInt;
public IntegerbInteger = newInteger(4);
public StringstrB = "crazypebble";
private StringstrA;
//构造函数
publicReflectionTest() {
}
protectedReflectionTest(int id, Stringname) {
}
//成员方法
publicintabc(int id, Stringname) {
System.out.println("crazypebble ---> " + id + "-" +name);
return0;
}
protectedstaticvoidedf() {
}
@Override
publicvoidactionPerformed(ActionEvent e) {
//TODOAuto-generated method stub
}
}
二、反射机制中需要使用到的类
我把需要使用的类列在下表中,其中对我们特别有用的类,通过着重标记显示出来,并将在后面的使用中逐步解释:
三、Class类
首先向大家说明一点,Class本身就是一个类,Class是该类的名称。看下面这个类的定义:
public
Class类是整个Java反射机制的源头,Class类本身表示Java对象的类型,我们可通过一个Object对象的getClass()方法取得一个对象的类型,此函数返回的就是一个Class类。获取Class对象的方法有很多种:
在平时的使用,要注意对这几种方法的灵活运用,尤其是对Class.forName()方法的使用。因为在很多开发中,会直接通过类的名称取得Class类的对象。
四、获取类的相关信息
1、获取构造方法
Class类提供了四个public方法,用于获取某个类的构造方法。
Constructor
Constructor
Constructor
Constructor
由于Java语言是一种面向对象的语言,具有多态的性质,那么我们可以通过构造方法的参数列表的不同,来调用不同的构造方法去创建类的实例。同样,获取不同的构造方法的信息,也需要提供与之对应的参数类型信息;因此,就产生了以上四种不同的获取构造方法的方式。
publicstaticvoidget_Reflection_Constructors(ReflectionTest r) {
Class temp = r.getClass();
String className = temp.getName(); // 获取指定类的类名
try{
Constructor[] theConstructors = temp.getDeclaredConstructors();//获取指定类的公有构造方法
for(inti = 0; i < theConstructors.length; i++) {
intmod = theConstructors[i].getModifiers(); // 输出修饰域和方法名称
System.out.print(Modifier.toString(mod) + " " + className +"(");
Class[] parameterTypes = theConstructors[i].getParameterTypes();//获取指定构造方法的参数的集合
for(intj = 0; j < parameterTypes.length; j++) { // 输出打印参数列表
System.out.print(parameterTypes[j].getName());
if(parameterTypes.length > j+1) {
System.out.print(", ");
}
}
System.out.println(")");
}
} catch (Exceptione) {
e.printStackTrace();
}
}
2、获取类的成员方法
与获取构造方法的方式相同,存在四种获取成员方法的方式。
Method
Method[]
Method
Method[]
publicstaticvoidget_Reflection_Method(ReflectionTest r) {
Class temp = r.getClass();
String className = temp.getName();
//Method[] methods =temp.getDeclaredMethods();
Method[] methods =temp.getMethods();
for(inti = 0; i < methods.length; i++) {
//打印输出方法的修饰域
int mod =methods[i].getModifiers();
System.out.print(Modifier.toString(mod) + " ");
//输出方法的返回类型
System.out.print(methods[i].getReturnType().getName());
//获取输出的方法名
System.out.print(" " + methods[i].getName() +"(");
//打印输出方法的参数列表
Class[] parameterTypes =methods[i].getParameterTypes();
for(intj = 0; j < parameterTypes.length; j++) {
System.out.print(parameterTypes[j].getName());
if(parameterTypes.length > j+1) {
System.out.print(", ");
}
}
System.out.println(")");
}
}
在获取类的成员方法时,有一个地方值得大家注意,就是getMethods()方法和getDeclaredMethods()方法。
getMethods():用于获取类的所有的public修饰域的成员方法,包括从父类继承的public方法和实现接口的public方法;
getDeclaredMethods():用于获取在当前类中定义的所有的成员方法和实现的接口方法,不包括从父类继承的方法。
大家可以查考一下开发文档的解释:
因此在示例代码的方法get_Reflection_Method(...)中,ReflectionTest类继承了Object类,实现了actionPerformed方法,并定义如下成员方法:
通过这两个语句执行后的结果不同:
a、Method[]
b、Method[]
3、获取类的成员变量(成员属性)
存在四种获取成员属性的方法
Field
Field[]
Field
Field[]
publicstaticvoidget_Reflection_Field_Value(ReflectionTest r) {
Class temp = r.getClass(); // 获取Class类的对象的方法之一
try{
System.out.println("public 属性");
Field[] fb = temp.getFields();
for(inti = 0; i < fb.length; i++) {
Class cl = fb[i].getType(); // 属性的类型
intmd = fb[i].getModifiers(); // 属性的修饰域
Field f = temp.getField(fb[i].getName()); // 属性的值
f.setAccessible(true);
Object value = (Object)f.get(r);
//判断属性是否被初始化
if (value ==null){
System.out.println(Modifier.toString(md) + " " + cl + " : " +fb[i].getName());
}
else{
System.out.println(Modifier.toString(md) + " " + cl + " : " +fb[i].getName() + " = " + value.toString());
}
}
System.out.println("public & 非public 属性");
Field[] fa = temp.getDeclaredFields();
for(inti = 0; i < fa.length; i++) {
Class cl = fa[i].getType(); // 属性的类型
intmd = fa[i].getModifiers(); // 属性的修饰域
Field f = temp.getDeclaredField(fa[i].getName()); // 属性的值
f.setAccessible(true);//VeryImportant
Object value = (Object)f.get(r);
if(value == null){
System.out.println(Modifier.toString(md) + " " + cl + " : " +fa[i].getName());
}
else{
System.out.println(Modifier.toString(md) + " " + cl + " : " +fa[i].getName() + " = " + value.toString());
}
}
} catch (Exceptione) {
e.printStackTrace();
}
}
4、获取类、属性、方法的修饰域
类Class、Method、Constructor、Field都有一个public方法int
在开发文档中,可以查阅到,Modifier类中定义了若干特定的修饰域,每个修饰域都是一个固定的int数值,列表如下:
该类不仅提供了若干用于判断是否拥有某中修饰域的方法boolean
五、如何调用类中的private方法
在介绍之前,先放一个代码吧,这段代码是参考其他文章的代码拷贝过来的,代码不算长,但是动态调用类的成员方法的过程讲解的通俗易懂。
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Method;
publicclass LoadMethod{
public ObjectLoad(String cName, String MethodName, String[] types, String[]params) {
Object retObject = null;
try{
//加载指定的类
Class cls = Class.forName(cName); // 获取Class类的对象的方法之二
// 利用newInstance()方法,获取构造方法的实例
// Class的newInstance方法只提供默认无参构造实例
// Constructor的newInstance方法提供带参的构造实例
Constructor ct = cls.getConstructor(null);
Object obj = ct.newInstance(null);
//Object obj =cls.newInstance();
// 构建方法的参数类型
Class paramTypes[] = this.getMethodTypesClass(types);
//在指定类中获取指定的方法
Method meth = cls.getMethod(MethodName,paramTypes);
//构建方法的参数值
Object argList[] = this.getMethodParamObject(types,params);
//调用指定的方法并获取返回值为Object类型
retObject = meth.invoke(obj, argList);
} catch (Exceptione) {
System.err.println(e);
}
returnretObject;
}
public Class[]getMethodTypesClass(String[] types) {
Class[] cs = newClass[types.length];
for(inti = 0; i < cs.length; i++) {
if(types[i] != null ||!types[i].trim().equals("")) {
if(types[i].equals("int") || types[i].equals("Integer")) {
cs[i] = Integer.TYPE;
}
elseif(types[i].equals("float") || types[i].equals("Float")) {
cs[i] = Float.TYPE;
}
elseif(types[i].equals("double") || types[i].equals("Double")){
cs[i] = Double.TYPE;
}
elseif(types[i].equals("boolean") || types[i].equals("Boolean")){
cs[i] = Boolean.TYPE;
}
else{
cs[i] = String.class;
}
}
}
returncs;
}
public Object[]getMethodParamObject(String[] types, String[] params) {
Object[] retObjects = newObject[params.length];
for(inti = 0; i < retObjects.length; i++) {
if(!params[i].trim().equals("")||params[i]!=null){
if(types[i].equals("int")||types[i].equals("Integer")){
retObjects[i]= newInteger(params[i]);
}
elseif(types[i].equals("float")||types[i].equals("Float")){
retObjects[i]= newFloat(params[i]);
}
elseif(types[i].equals("double")||types[i].equals("Double")){
retObjects[i]= newDouble(params[i]);
}
elseif(types[i].equals("boolean")||types[i].equals("Boolean")){
retObjects[i]=newBoolean(params[i]);
}
else{
retObjects[i] = params[i];
}
}
}
returnretObjects;
}
}
要调用一个类的方法,首先需要一个该类的实例(当然,如果该类是static,就不需要实例了,至于原因,你懂得!)。
1、创建一个类的实例
在得到一个类的Class对象之后,我们可以利用类Constructor去实例化该对象。Constructor支持泛型,也就是它本身应该是Constructor。这个类有一个public成员函数:T
在代码LoadMethod.java和LoadMethodEx.java中,分别给出了两种实例化Class类的方法:一种是利用Constructor类调用newInstance()方法;另一种就是利用Class类本身的newInstance()方法创建一个实例。两种方法实现的效果是一样的。
// Class的newInstance方法,仅提供默认无参的实例化方法,类似于无参的构造方法
// Constructor的newInstance方法,提供了带参数的实例化方法,类似于含参的构造方法
Constructor ct = cls.getConstructor(null);
Object obj = ct.newInstance(null);
Object obj = cls.newInstance();
2、行为
Method类中包含着类的成员方法的信息。在Method类中有一个public成员函数:Object
如果某一个方法是Java类的静态方法,那么Object
对类的成员变量进行读写,在Field类中有两个public方法:
Object
Void
其中,Object参数是需要传入的对象;如果成员变量是静态属性,在object可传入null。
六、对LoadMethod.java的优化处理
在上一节中给出的LoadMethod.java中,类LoadMethod对固定参数类型的方法进行了调用,并且参数类型是通过一个String[]数组传入,然后经过方法
因此,我对LoadMethod类进行了一定的优化处理。先附上代码:
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Method;
publicclassLoadMethodEx {
public ObjectLoad(String cName, String MethodName, Object[] params) {
Object retObject = null;
try{
//加载指定的类
Class cls = Class.forName(cName); // 获取Class类的对象的方法之二
// 利用newInstance()方法,获取构造方法的实例
// Class的newInstance方法只提供默认无参构造实例
// Constructor的newInstance方法提供带参的构造实例
Constructor ct = cls.getConstructor(null);
Object obj = ct.newInstance(null);
//Object obj =cls.newInstance();
// 根据方法名获取指定方法的参数类型列表
Class paramTypes[] = this.getParamTypes(cls,MethodName);
//获取指定方法
Method meth = cls.getMethod(MethodName,paramTypes);
meth.setAccessible(true);
//调用指定的方法并获取返回值为Object类型
retObject = meth.invoke(obj, params);
} catch (Exceptione) {
System.err.println(e);
}
returnretObject;
}
public Class[]getParamTypes(Class cls, String mName) {
Class[] cs = null;
Method[] mtd = cls.getDeclaredMethods();
for(inti = 0; i < mtd.length; i++) {
if(!mtd[i].getName().equals(mName)) { // 不是我们需要的参数,则进入下一次循环
continue;
}
cs = mtd[i].getParameterTypes();
}
returncs;
}
}
如果我们已经知道某个类名和需要动态调用的方法名,怎样才能不用传入方法的参数类型就可以调用该方法呢?
1、LoadMethodEx类,少了一个参数(方法参数类型列表),本文直接从类LoadMethod内部获取该参数类型列表,不需要用户传入该信息,好处其实也不言而喻了。
2、方法的参数值:类LoadMethod是将所有的方法参数都做为一个String来传入,在传入再进行解析;而本文则直接使用Object类型做为参数类型,因为invoke(Object
在调用LoadMethod的Load()方法时,用户只需要知道类名、方法名,并且将已经初始化的参数先向上转型为Object,然后传递给Load()方法即可。方法的返回值为Object,这个肯定是由用户根据自己的需要,再转换成自己所需的类型。
public属性
publicclassjava.lang.Integer : bInteger = 4
publicclassjava.lang.String : strB = crazypebble
public &非public 属性
privateint: bInt = 0
publicclassjava.lang.Integer : bInteger = 4
publicclassjava.lang.String : strB = crazypebble
privateclassjava.lang.String : strA
构造方法:
publiccrazypebble.reflectiontest.ReflectionTest()
protectedcrazypebble.reflectiontest.ReflectionTest(int,java.lang.String)
父类/接口:
父类: java.lang.Object
接口0: java.awt.event.ActionListener
接口1: java.io.Serializable
成员方法:
publicintabc(int,java.lang.String)
publicvoidactionPerformed(java.awt.event.ActionEvent)
publicfinalnativevoidwait(long)
publicfinalvoidwait()
publicfinalvoidwait(long,int)
publicbooleanequals(java.lang.Object)
publicjava.lang.String toString()
publicnativeinthashCode()
publicfinalnativejava.lang.Class getClass()
publicfinalnativevoidnotify()
publicfinalnativevoidnotifyAll()
反射机制调用方法:LoadMethod
crazypebble ---> 1-hello, android-1!
反射机制调用方法:LoadMethodEx
crazypebble ---> 2-hello, android-2?
返回结果:0
七、总结
关于反射机制,其实还有一个比较敏感的话题,就是反射机制带来我们的安全性问题。由于我在这方面研究的不是很深入,所以讲不好。大家有空可以跟踪一下在本文最后提供的两个链接,里面有一些介绍。
我们介绍了Java的反射机制,但是在Android平台下,反射机制具体有没有什么用途呢?答案是肯定的。推荐大家看一篇文章《利用Java反射技术阻止通过按钮关闭对话框》,这篇文章为CSDN推荐为精品文章,所以还是很值得一看的。我特地从CSDN转载过来供大家一起学习。
原链接:http://blog.csdn.net/nokiaguy/archive/2010/07/27/5770263.aspx
转载链接:http://www.cnblogs.com/crazypebble/archive/2011/04/13/2014297.html
程序实现的源码:
importjava.lang.reflect.Field;
importandroid.app.Activity;
importandroid.app.AlertDialog;
importandroid.content.DialogInterface;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
publicclassMainActivity extends Activity{
privatestatic ButtonbtnHandler = null;
privatestatic ButtonbtnShowing = null;
AlertDialog alertDialog = null;
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnHandler = (Button)findViewById(R.id.btn_mHandler);
btnHandler.setOnClickListener(newButtonListener());
btnShowing = (Button)findViewById(R.id.btn_mShowing);
btnShowing.setOnClickListener(newButtonListener());
alertDialog = newAlertDialog.Builder(this)
.setTitle("abc")
.setMessage("Content")
.setIcon(R.drawable.icon)
.setPositiveButton("确定", newPositiveClickListener())
.setNegativeButton("取消", newNegativeClickListener())
.create();
}
privateclassButtonListener implementsOnClickListener {
@Override
publicvoidonClick(View v) {
switch(v.getId()) {
caseR.id.btn_mHandler:
modify_mHandler();
alertDialog.show();
break;
caseR.id.btn_mShowing:
alertDialog.show();
break;
default:
break;
}
}
}
privateclassPositiveClickListener implementsandroid.content.DialogInterface.OnClickListener {
@Override
publicvoidonClick(DialogInterface dialog, int which){
//方法二时启用
modify_dismissDialog(false);
}
}
privateclassNegativeClickListener implementsandroid.content.DialogInterface.OnClickListener {
@Override
publicvoidonClick(DialogInterface dialog, int which){
//方法一时启用
//dialog.dismiss();
// 方法二时启用
modify_dismissDialog(true);
}
}
publicvoidmodify_mHandler() {
try{
Field field =alertDialog.getClass().getDeclaredField("mAlert");
field.setAccessible(true);
//获取mAlert变量的值
Object obj = field.get(alertDialog);
field = obj.getClass().getDeclaredField("mHandler");
field.setAccessible(true);
//修改mHandler变量的值,使用新的ButtonHandler类
field.set(obj, newMyButtonHandler(alertDialog));
} catch (Exceptione) {
e.printStackTrace();
}
}
publicvoidmodify_dismissDialog(boolean flag){
try{
Field field =alertDialog.getClass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
//将mShowing变量设为false,表示对话框已经关闭
field.set(alertDialog, flag);
alertDialog.dismiss();
} catch (Exceptione) {
e.printStackTrace();
}
}
}
importjava.lang.ref.WeakReference;
importandroid.content.DialogInterface;
importandroid.os.Handler;
importandroid.os.Message;
publicclassMyButtonHandler extendsHandler{
//Buttonclicks have Message.what as the BUTTON{1,2,3}constant
privatestaticfinalintMSG_DISMISS_DIALOG = 1;
privateWeakReference mDialog;
publicMyButtonHandler(DialogInterface dialog) {
mDialog = newWeakReference(dialog);
}
@Override
publicvoidhandleMessage(Message msg) {
switch(msg.what) {
caseDialogInterface.BUTTON_POSITIVE:
caseDialogInterface.BUTTON_NEGATIVE:
caseDialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(),msg.what);
break;
}
}
}
看完上面这篇文章之后,希望大家明确一点的是:反射机制通过void
- Android反射机制实现与原理[转]
- (转)Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android之反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- Android反射机制实现与原理
- 使用 Tortoise SVN 创建 Ext…
- 人人都能看懂——c大调d大调f…
- (转)Neither user 10027…
- STK App源码解析
- WCDMA与TDD&FDD
- (转)Android反射机制实现与原理
- hdu 1043 Eight(bfs+康托)
- Kernel driver not installe…
- 4G(LTE)背后的技术和利益…
- ubuntu12.04与VM Virtual&nbs…
- 简单字符串排序
- linux文件锁
- [JNI]开发之旅(6)JNI函数中访问java类中对象的属性
- HEOI2016排序-解题报告