首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

浅析Java反照机制

2012-09-06 
浅析Java反射机制如果你感觉到图片里有东西在转动,那么在看完这篇博客之后休息吧,你已经累了。反射,很容易

浅析Java反射机制



     如果你感觉到图片里有东西在转动,那么在看完这篇博客之后休息吧,你已经累了。

     反射,很容易让人与RTTI混淆起来。虽然二者都是获取类型信息的机制,但是二者是存在本质区别的。RTTI(Run-Time Type Information,运行时类型信息)是在编译时获取.class文件,而反射机制在编译阶段是获取不到.class文件的,只有在运行时才能去得到.class文件(当然也有可能找不到)。
     接下来,将全面介绍一下反射机制。

     什么是反射?
    反射,是个简单且神奇的东西。说它简单,是因为它真的不难,虽然很多人一直觉得反射的概念很抽象,而且在普通的编程中很少用到,但是只要鼓起勇气往前走几步,就会看到,原来反射就是用来检查未知类中的基本信息,并予以返回。Eckel曰过:当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类。看吧,仅仅是检查一下而已,简单吧。说它神奇,是因为反射可以让我们获取进行中的类的基本信息,这在C和C++里是不可想象的,而且反射也可以实现RMI(Remote Method Invoke,远程方法调用),我们可以呆在北京调用纽约的类创建和运用对象,想想就觉得神奇额,世界因为我们而变小,不愧是技术宅拯救全世界。我给反射下的定义是:反射,就是获取运行中的程序中一个未知类的基本的信息并对这些基本信息进行操作的机制。

    反射怎么用?
     那么,既然反射这么有用,要怎么用呢?
     RTTI中得到类型信息有两种方法:Class.forName()和使用类字面常量。在反射机制里,考虑到我们在编译时不知道该类是否存在,因此类字面常量的使用在反射里就无从说起了,我们在反射里使用的是Class.forName()方法(而在RTTI中,使用类字面常量效果更好),你会不会在想:如果找不到这个类,会不会在编译时抛出ClassNotFoundException呢?答案是No。因为forName方法在编译时的结果是不可知的。具体的使用,我们将通过以下几个示例介绍:

// 父类class Father{String fName;public int a;public Father(){}public Father(String fName){this.fName = fName;} // end constructorvoid fFun1(){System.out.println("Father.fFun1()");}void fFun2(String str){System.out.println("Father.fFun2()" + str);}public int fFun3(){System.out.println("Father.fFun3()");return 0;}} // end class Father// 子类class Son extends Father{String sStr;public Son(String sStr){this.sStr = sStr;}public void sFun1(){System.out.println("Son.sFun1()");}public void sFun2(int i){System.out.println("Son.sFun2()");}void sFun3(){System.out.println("Son.sFun3()");}} // end class Son

     以上就是两个测试类的声明。由于反射的概念是靠Class类和java.lang.reflect类库支撑起来的。因此我们要得到类的信息,就需要借助Method、Field以及Constructor等类。首先,我们需要先得到一个Class对象(我们先假设我们不知道这个类里面有什么方法和属性,甚至不知道有木有这个.class文件):
Class c = Class.forName("com.tuyage.typeInfo.Son");

forName方法的参数是Son类的全限定名。接下来,我们先试图得到Son对象的构造器:
Constructor[] cons1 = c.getConstructors();Constructor[] cons2 = c.getDeclaredConstructors();System.out.println("#c.getConstructors()");for(Constructor con: cons1){System.out.println(con.toString());} // end forSystem.out.println("#c.getDeclaredConstructors()");for(Constructor con : cons2){System.out.println(con.toString());} // end for

     可以看到,在这里,我们有两个取得构造器的方法:getConstructors()和getDeclaredConstructors()。等会也会看到Method和Field也有两种方法,下面就是这段代码运行的结果:
     #c.getConstructors()
     public com.tuyage.typeInfo.Son(java.lang.String)
     #c.getDeclaredConstructors()
     public com.tuyage.typeInfo.Son(java.lang.String)
     两个结果是一样的,其实是有区别的,getConstructors()方法返回的是当前类的所有public的构造器,而getDeclaredConstructors()方法返回的是当前类的所有构造器。此处表现不明显,大家可以自己动手尝试。
     接着看Method的获取:
Method[] meth1 = c.getMethods();Method[] meth2 = c.getDeclaredMethods();System.out.println("#c.getMethods()");for(Method m: meth1){System.out.println(m.toString());} // end forSystem.out.println("#c.getDeclaredMethods()");for(Method m : meth2){System.out.println(m.toString());} // end for

    看看运行的结果:
     #c.getMethods()
     public void com.tuyage.typeInfo.Son.sFun1()
        public void com.tuyage.typeInfo.Son.sFun2(int)
     public int com.tuyage.typeInfo.Father.fFun3()
     public final native java.lang.Class java.lang.Object.getClass()
     public native int java.lang.Object.hashCode()
     public boolean java.lang.Object.equals(java.lang.Object)
     public java.lang.String java.lang.Object.toString()
     public final native void java.lang.Object.notify()
     public final native void java.lang.Object.notifyAll()
     public final void java.lang.Object.wait() throws java.lang.InterruptedException
     public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
     public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
     #c.getDeclaredMethods()
     public void com.tuyage.typeInfo.Son.sFun1()
     public void com.tuyage.typeInfo.Son.sFun2(int)
     void com.tuyage.typeInfo.Son.sFun3()
     看到没,有很多方法名字冒出来了,而且还有很多我们很熟悉的,比如toString()、notify()等等,它们都是Object类的方法,因为,getMethods()方法得到的是该类及其父类的所有public方法,Object作为万物之父,它的方法出现在这里也就不足为奇了。大家还可以看到void com.tuyage.typeInfo.Son.sFun3()方法只出现在了getDeclaredMethods()方法里,因为这个方法是用来得到当前类所有方法的。
     接下来,轮到Field出场了,当然是大同小异了,请看代码:
Field[] field1 = c.getFields();Field[] field2 = c.getDeclaredFields();System.out.println("#c.getFields()");for(Field f: field1){System.out.println(f.toString());} // end forSystem.out.println("#c.getDeclaredFields()");for(Field f : field2){System.out.println(f.toString());} // end for

     运行结果如下:
     #c.getFields()
     public int com.tuyage.typeInfo.Father.a
     #c.getDeclaredFields()
     java.lang.String com.tuyage.typeInfo.Son.sStr
     这个与Method的测试结果又有出入,使用getFields()方法时,并没有出现本类的field,而是父类的public属性,而getDeclaredFields()方法还是一样返回本类的所有属性(此处不明显,大家可以加几个属性试试)。
     当然,建议大家一般使用getDeclaredXXXXX()方法。

     小贴士:
     main方法也可以被继承,因此也会在子类getMethods()方法的结果中也会出现(前提是父类有main方法,但是一般没有人这么做);
     获得恰当的Class对象的引用是反射和RTTI的前提和基石;
     Class对象仅在需要的时候才加载;
     java.lang.reflect类库中的类的对象是由JVM在运行时创建的。


     当然,以上还是只是反射的一点点皮毛,还有其他很多东西在此没有一一列举出来。如果想要真正的理解反射,还得多看看相关的资料,多动手实践。另外一个方法就是看java.lang.reflect类库和Class类的源代码了。反射的精彩,等着大家一起去探索! 嗯,谢谢。

热点排行