首页 > 代码库 > 5.8 反射机制

5.8 反射机制

  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) { }
    }

 

5.8 反射机制