JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射机制主要提供了以下功能:
- 在运行时判定任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判定任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
简单说,反射机制值得是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。
在 JDK 中,主要由以下类来实现Java 反射机制,这些类都位于java.lang.reflect包中。
- Class类:代表一个类。
- Field类:代表类的成员变量(成员变量也称为类的属性)。
- Method类:代表类的方法。
- Constructor 类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数组元素的静态方法。
Java 反射机制是Java 语言的一个重要特性。考虑实现一个newInstance(String className)方法,它的作用是根据参数className 指定的类名,通过该类的不带参数的构造方法创建这个类的对象,并将其返回。如果不运用Java 反射机制,必须在newInstance()方法中罗列参数className所有可能的取值,然后创建相应的对象:
public Object newInstance(String className) throws Exception{ if(className.equals("HelloService1"))
return new HelloService1(); if(className.equals("HelloService2"))
return new HelloService2(); //... if(className.equals("HelloService1000"))
return new HelloService1000(); }
以上程序代码很冗长,而且可维护性差。如果在以后软件的升级版本中去除了一个HelloService4类,或者增加了一个HelloService1001类,都需要修改以上newInstance()方法。
如果运用反射机制,就可以简化程序代码,并且提高软件系统的可维护性和可扩展性:
public Object newInstance(String className) throws Exception{ Class classType = Class.forName(className); return classType.newInstance(); }
我们在进行Android程序的开发时,为了方便调试程序,并快速定位程序的错误点,会从网上下载到对应版本的Android SDK的源码(这里给大家提供一个2.3.3版本的下载链接)。你会发现很多类或方法中经常加上了“@hide”注释标记,它的作用是使这个方法或类在生成SDK时不可见,那么我们的程序可能无法编译通过,而且在最终发布的时候,就可能存在一些问题。
那么,对于这个问题,第一种方法就是自己去掉Android源码中的"@hide"标记,然后重新编译生成一个SDK。另一种方法就是使用Java反射机制了,可以利用这种反射机制访问存在访问权限的方法或修改其域。
反射机制的优缺点?
- 静态编译:在编译时确定类型,绑定对象,即通过
- 动态编译:运行时确定类型,绑定对象。动态编译最大限度的发挥了java的灵活性,体现了多态的应用,有利于降低类之间的耦合性。
反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
下面给两个小例子:
例1:执行另外一个包里面的某个类的方法,另外一个包的包名是chroya.demo,类名Main,方法名print:
package chroya.demo; import android.app.Activity; import android.os.Bundle; import android.util.Log; class Main extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } public void print(String msg) { Log.d("Main", "msg:"+ msg); } }
本包调用Main的print方法的代码块如下:
Context c = createPackageContext("chroya.demo", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); //载入这个类 Class clazz = c.getClassLoader().loadClass("chroya.demo.Main"); //新建一个实例 Object owner = clazz.newInstance(); //获取print方法,传入参数并执行 Object obj = clazz.getMethod("print", String.class).invoke(owner, "Hello");
例2:永不消失的Toast
public class MainActivity extends Activity implements OnClickListener{ Toast myToast; TextView vt; Object obj; Field field; Field fieldview; Method methodshow; Method methodhide; @Override protected void onCreate(Bundle savedInstanceState) { …… // 先创建一个Toast对象 myToast = new Toast(this); vt = new TextView(this); vt.setText("永不消失的Toast"); myToast.setView(vt); } void showToast(){ try { // 从Toast对象中获得mTN变量 field = myToast.getClass().getDeclaredField("mTN"); field.setAccessible(true); // setAccessible(true)可以访问private域 obj = field.get(myToast); fieldview = obj.getClass().getDeclaredField("mNextView"); fieldview.setAccessible(true); fieldview.set(obj, vt); // TN对象中获得了show方法 methodshow = obj.getClass().getDeclaredMethod("show", null); methodhide = obj.getClass().getDeclaredMethod("hide", null); methodshow.invoke(obj, null); } catch (Exception e) { } }