Java反射机制的原理及在Android下的简单应用.doc_第1页
Java反射机制的原理及在Android下的简单应用.doc_第2页
Java反射机制的原理及在Android下的简单应用.doc_第3页
Java反射机制的原理及在Android下的简单应用.doc_第4页
Java反射机制的原理及在Android下的简单应用.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Java反射机制的原理及在Android下的简单应用花了几天时间,研究了一下Java的反射机制。在这里总结一下这几天学习的成果,一来分享自己的学习过程和在学习中遇到的问题,二来是给像我一样不太了解Java反射机制的同学做一个简单的介绍。在文章后面会链接一个Android反射机制的应用程序。一、反射的概念及在Java中的类反射反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。在计算机科学领域,反射是一类应用,它们能够自描述和自控制。这类应用通过某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。在Java中的反射机制,被称为Reflection。(大家看到这个单词,第一个想法应该就是去开发文档中搜一下了。)它允许运行中的Java程序对自身进行检查,并能直接操作程序的内部属性或方法。Reflection机制允许程序在正在执行的过程中,利用ReflectionAPIs取得任何已知名称的类的内部信息,包括:package、typeparameters、superclass、implementedinterfaces、innerclasses、outerclasses、fields、constructors、methods、modifiers等,并可以在执行的过程中,动态生成Instances、变更fields内容或唤起methods。好,了解这些,那我们就知道了,我们可以利用反射机制在Java程序中,动态的去调用一些protected甚至是private的方法或类,这样可以很大程度上满足我们的一些比较特殊需求。你当然会问,反射机制在Android平台下有何用处呢?我们在进行Android程序的开发时,为了方便调试程序,并快速定位程序的错误点,会从网上下载到对应版本的AndroidSDK的源码(这里给大家提供一个2.3.3版本的下载链接)。你会发现很多类或方法中经常加上了“hide”注释标记,它的作用是使这个方法或类在生成SDK时不可见,那么我们的程序可能无法编译通过,而且在最终发布的时候,就可能存在一些问题。那么,对于这个问题,第一种方法就是自己去掉Android源码中的hide标记,然后重新编译生成一个SDK。另一种方法就是使用Java反射机制了,可以利用这种反射机制访问存在访问权限的方法或修改其域。废话半天,该入正题了,在进入正题之前,先给上一个反射测试类的代码,该代码中定义了我们需要进行反射的类,该类并没有实际的用途,仅供做为测试类。提示:本文提供的代码,并不是Android平台下的代码,而是一个普通的Java程序,仅仅是对Java反射机制的Demo程序,所以大家不要放在Android下编译啊,否则出现问题,别追究我的责任啦!ReflectionTest.javapackage crazypebble.reflectiontest;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.Serializable;public class ReflectionTest extends Object implements ActionListener,Serializable / 成员变量 private int bInt; public Integer bInteger = new Integer(4); public String strB = crazypebble; private String strA; / 构造函数 public ReflectionTest() protected ReflectionTest(int id, String name) / 成员方法 public int abc(int id, String name) System.out.println(crazypebble - + id + - + name); return 0; protected static void edf() Override public void actionPerformed(ActionEvent e) / TODO Auto-generated method stub 二、反射机制中需要使用到的类我把需要使用的类列在下表中,其中对我们特别有用的类,通过着重标记显示出来,并将在后面的使用中逐步解释:三、Class类首先向大家说明一点,Class本身就是一个类,Class是该类的名称。看以下下面这个类的定义:publicclassMyButtonextendsButton.注意到上面的class的首字母是小写,它表示的是一种类类型,但是我们的Class是一个类,相当于上面定义的MyButton类。所以,千万不要把这里的Class做为一个类类型来理解。明白这一点,我们继续。Class类是整个Java反射机制的源头,Class类本身表示Java对象的类型,我们可通过一个Object对象的getClass()方法取得一个对象的类型,此函数返回的就是一个Class类。获取Class对象的方法有很多种:在平时的使用,要注意对这几种方法的灵活运用,尤其是对Class.forName()方法的使用。因为在很多开发中,会直接通过类的名称取得Class类的对象。四、获取类的相关信息1、获取构造方法Class类提供了四个public方法,用于获取某个类的构造方法。ConstructorgetConstructor(Classparams) 根据构造函数的参数,返回一个具体的具有public属性的构造函数ConstructorgetConstructors() 返回所有具有public属性的构造函数数组ConstructorgetDeclaredConstructor(Classparams) 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)ConstructorgetDeclaredConstructors() 返回该类中所有的构造函数数组(不分public和非public属性)由于Java语言是一种面向对象的语言,具有多态的性质,那么我们可以通过构造方法的参数列表的不同,来调用不同的构造方法去创建类的实例。同样,获取不同的构造方法的信息,也需要提供与之对应的参数类型信息;因此,就产生了以上四种不同的获取构造方法的方式。get_Reflection_Constructors() /* * 获取反射类中的构造方法 * 输出打印格式:Modifier修饰域 构造方法名(参数类型列表) */ public static void get_Reflection_Constructors(ReflectionTest r) Class temp = r.getClass(); String className = temp.getName(); / 获取指定类的类名 try Constructor theConstructors = temp.getDeclaredConstructors(); / 获取指定类的公有构造方法 for (int i = 0; i theConstructors.length; i+) int mod = theConstructorsi.getModifiers(); / 输出修饰域和方法名称 System.out.print(Modifier.toString(mod) + + className + (); Class parameterTypes = theConstructorsi.getParameterTypes(); / 获取指定构造方法的参数的集合 for (int j = 0; j j+1) System.out.print(, ); System.out.println(); catch (Exception e) e.printStackTrace(); 2、获取类的成员方法与获取构造方法的方式相同,存在四种获取成员方法的方式。MethodgetMethod(Stringname,Classparams) 根据方法名和参数,返回一个具体的具有public属性的方法MethodgetMethods() 返回所有具有public属性的方法数组MethodgetDeclaredMethod(Stringname,Classparams) 根据方法名和参数,返回一个具体的方法(不分public和非public属性)MethodgetDeclaredMethods() 返回该类中的所有的方法数组(不分public和非public属性)get_Reflection_Method() /* * 获取反射类的方法 * 打印输出格式:RetType FuncName(paramTypeList) */ public static void get_Reflection_Method(ReflectionTest r) Class temp = r.getClass(); String className = temp.getName(); /* * Note: 方法getDeclaredMethods()只能获取到由当前类定义的所有方法,不能获取从父类继承的方法 * 方法getMethods() 不仅能获取到当前类定义的public方法,也能得到从父类继承和已经实现接口的public方法 * 请查阅开发文档对这两个方法的详细描述。 */ /Method methods = temp.getDeclaredMethods(); Method methods = temp.getMethods(); for (int i = 0; i methods.length; i+) / 打印输出方法的修饰域 int mod = methodsi.getModifiers(); System.out.print(Modifier.toString(mod) + ); / 输出方法的返回类型 System.out.print(methodsi.getReturnType().getName(); / 获取输出的方法名 System.out.print( + methodsi.getName() + (); / 打印输出方法的参数列表 Class parameterTypes = methodsi.getParameterTypes(); for (int j = 0; j j+1) System.out.print(, ); System.out.println(); 在获取类的成员方法时,有一个地方值得大家注意,就是getMethods()方法和getDeclaredMethods()方法。getMethods():用于获取类的所有的public修饰域的成员方法,包括从父类继承的public方法和实现接口的public方法;getDeclaredMethods():用于获取在当前类中定义的所有的成员方法和实现的接口方法,不包括从父类继承的方法。大家可以查考一下开发文档的解释:getMethods()-ReturnsanarraycontainingMethodobjectsforallpublicmethodsfortheclassCrepresentedbythisClass.MethodsmaybedeclaredinC,theinterfacesitimplementsorinthesuperclassesofC.Theelementsinthereturnedarrayareinnoparticularorder.getDeclaredMethods()-ReturnsaMethodobjectwhichrepresentsthemethodmatchingthespecifiednameandparametertypes thatisdeclaredbytheclassrepresentedbythisClass.因此在示例代码的方法get_Reflection_Method(.)中,ReflectionTest类继承了Object类,实现了actionPerformed方法,并定义如下成员方法:通过这两个语句执行后的结果不同:a、Methodmethods=temp.getDeclaredMethods()执行后结果如下:b、Methodmethods=temp.getMethods()执行后,结果如下:3、获取类的成员变量(成员属性)存在四种获取成员属性的方法FieldgetField(Stringname) 根据变量名,返回一个具体的具有public属性的成员变量FieldgetFields() 返回具有public属性的成员变量的数组FieldgetDeclaredField(Stringname) 根据变量名,返回一个成员变量(不分public和非public属性)FieldgetDelcaredField() 返回所有成员变量组成的数组(不分public和非public属性)get_Reflection_Field_Value() /* * 获取反射类中的属性和属性值 * 输出打印格式:Modifier Type : Name = Value * Note: 对于未初始化的指针类型的属性,将不输出结果 */ public static void get_Reflection_Field_Value(ReflectionTest r) Class temp = r.getClass(); / 获取Class类的对象的方法之一 try System.out.println(public 属性); Field fb = temp.getFields(); for (int i = 0; i fb.length; i+) Class cl = fbi.getType(); / 属性的类型 int md = fbi.getModifiers(); / 属性的修饰域 Field f = temp.getField(fbi.getName(); / 属性的值 f.setAccessible(true); Object value = (Object)f.get(r); / 判断属性是否被初始化 if (value = null) System.out.println(Modifier.toString(md) + + cl + : + fbi.getName(); else System.out.println(Modifier.toString(md) + + cl + : + fbi.getName() + = + value.toString(); System.out.println(public & 非public 属性); Field fa = temp.getDeclaredFields(); for (int i = 0; i fa.length; i+) Class cl = fai.getType(); / 属性的类型 int md = fai.getModifiers(); / 属性的修饰域 Field f = temp.getDeclaredField(fai.getName(); / 属性的值 f.setAccessible(true); / Very Important Object value = (Object) f.get(r); if (value = null) System.out.println(Modifier.toString(md) + + cl + : + fai.getName(); else System.out.println(Modifier.toString(md) + + cl + : + fai.getName() + = + value.toString(); catch (Exception e) e.printStackTrace(); 4、获取类、属性、方法的修饰域类Class、Method、Constructor、Field都有一个public方法intgetModifiers()。该方法返回一个int类型的数,表示被修饰对象(Class、Method、Constructor、Field)的修饰类型的组合值。在开发文档中,可以查阅到,Modifier类中定义了若干特定的修饰域,每个修饰域都是一个固定的int数值,列表如下:该类不仅提供了若干用于判断是否拥有某中修饰域的方法booleanisXXXXX(intmodifiers),还提供一个StringtoString(intmodifier)方法,用于将一个表示修饰域组合值的int数转换成描述修饰域的字符串。五、如何调用类中的private方法在介绍之前,先放一个代码吧,这段代码是参考其他文章的代码拷贝过来的,代码不算长,但是动态调用类的成员方法的过程讲解的通俗易懂。LoadMethod.javapackage crazypebble.reflectiontest;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class LoadMethod /* * 在运行时加载指定的类,并调用指定的方法 * param cName Java的类名 * param MethodName 方法名 * param types 方法的参数类型 * param params 方法的参数值 * return */ public Object Load(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 (Exception e) System.err.println(e); return retObject; /* * 获取参数类型,返回值保存在Class中 */ public Class getMethodTypesClass(String types) Class cs = new Classtypes.length; for (int i = 0; i cs.length; i+) if (typesi != null | !typesi.trim().equals() if (typesi.equals(int) | typesi.equals(Integer) csi = Integer.TYPE; else if (typesi.equals(float) | typesi.equals(Float) csi = Float.TYPE; else if (typesi.equals(double) | typesi.equals(Double) csi = Double.TYPE; else if (typesi.equals(boolean) | typesi.equals(Boolean) csi = Boolean.TYPE; else csi = String.class; return cs; /* * 获取参数Object */ public Object getMethodParamObject(String types, String params) Object retObjects = new Objectparams.length; for (int i = 0; i retObjects.length; i+) if(!paramsi.trim().equals()|paramsi!=null) if(typesi.equals(int)|typesi.equals(Integer) retObjectsi= new Integer(paramsi); else if(typesi.equals(float)|typesi.equals(Float) retObjectsi= new Float(paramsi); else if(typesi.equals(double)|typesi.equals(Double) retObjectsi= new Double(paramsi); else if(typesi.equals(boolean)|typesi.equals(Boolean) retObjectsi=new Boolean(paramsi); else retObjectsi = paramsi; return retObjects; 要调用一个类的方法,首先需要一个该类的实例(当然,如果该类是static,就不需要实例了,至于原因,你懂得!)。1、创建一个类的实例在得到一个类的Class对象之后,我们可以利用类Constructor去实例化该对象。Constructor支持泛型,也就是它本身应该是Constructor。这个类有一个public成员函数:TnewInstance(Object.args),其中args为对应的参数,我们通过Constructor的这个方法来创建类的对象实例。在代码LoadMethod.java和LoadMethodEx.java中,分别给出了两种实例化Class类的方法:一种是利用Constructor类调用newInstance()方法;另一种就是利用Class类本身的newInstance()方法创建一个实例。两种方法实现的效果是一样的。/ 利用newInstance()方法,获取构造方法的实例/ Class的newInstance方法,仅提供默认无参的实例化方法,类似于无参的构造方法/ Constructor的newInstance方法,提供了带参数的实例化方法,类似于含参的构造方法 Constructor ct = cls.getConstructor(null);Object obj = ct.newInstance(null);Object obj = cls.newInstance();2、行为Method类中包含着类的成员方法的信息。在Method类中有一个public成员函数:Objectinvoke(Objectreceiver,Object.args),参数receiver指明了调用对象,参数args指明了该方法所需要接收的参数。由于我们是在运行时动态的调用类的方法,无法提前知道该类的参数类型和返回值类型,所以传入的参数的类型是Object,返回的类型也是Object。(因为Object类是所有其他类的父类)如果某一个方法是Java类的静态方法,那么Objectreceiver参数可以传入null,因为静态方法从不属于对象。3、属性对类的成员变量进行读写,在Field类中有两个public方法:Objectget(Objectobject),该方法可用于获取某成员变量的值Voidset(Objectobject,Objectvalue),该方法设置某成员变量的值其中,Object参数是需要传入的对象;如果成员变量是静态属性,在object可传入null。六、对LoadMethod.java的优化处理在上一节中给出的LoadMethod.java中,类LoadMethod对固定参数类型的方法进行了调用,并且参数类型是通过一个String数组传入,然后经过方法getMethodTypesClass()解析之后,才得到了参数的具体的类型。同时在getMethodTypesClass()和getMethodParamObject()方法中,通过对传入的字符串参数进行过滤后,再处理那些可以匹配中的参数类型,其他不能匹配的参数都做为String对象来处理。如果我们调用的方法所需要的参数不是简单类型的变量,而是自定义的类对象,或者List列表,再如果我们只知道类名和方法名,不知道方法的参数类型,那我们该如何处理这些情况呢?因此,我对LoadMethod类进行了一定的优化处理。先附上代码:LoadMethodEx.javapackage crazypebble.reflectiontest;import java.lang.reflect.Constructor;import java.lang.reflect.Method;public class LoadMethodEx /* * 在运行时加载指定的类,并调用指定的方法 * param cName Java的类名 * param MethodName 方法名 * param params 方法的参数值 * return */ public Object Load(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); / 调用指定的方法并获取返

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论