1. 程式人生 > >JAVA反射機制及CLASS.FORNAME的作用及含義

JAVA反射機制及CLASS.FORNAME的作用及含義

最近由於工作上需要,對reflection做了一番瞭解,以下是學習總結,有不少內容是借鑑的,但已無法找到源文出處,還請原文作者見諒。

Reflection 是Java被視為動態(或準動態)語言的一個關鍵性質。這個機制允許程式在執行時透過Reflection APIs取得任何一個已知名稱的class

的內部資訊,包括其modifiers(諸如public, static 等等)、superclass(例如Object)、實現之interfaces(例如Cloneable),也包括fields

和methods的所有資訊,並可於執行時改變fields內容或喚起methods。

Java為什麼能夠支援Reflection?答案是Java執行時仍然擁有型別資訊,它包含了這個類一切:它有哪些欄位、哪些方法,各是何種保護級別等等,還有這個類依賴於哪些類。在Java中,類資訊以物件的形式存放,這些物件是一種元物件,它們的型別就是Class。擁有了這些資訊,無論是動態建立物件還是呼叫某些方法都是輕而易舉的。在C 中,通過RTTI(執行時型別識別),我們也可以知道類的一些資訊,但為什麼C 中卻沒有Reflection,原因是型別資訊不完整。RTTI這個名字本身就告訴我們,C 的型別資訊是用來進行型別識別的,因此,它也不需要其它額外的資訊。並不是C 無法做到這一點,而是C 不希望給使用者增加額外的負擔。有所得,必然有所失,因此,C 放棄了元物件。關於這一點,C 之父Bjarne Stroustrup在他的《C 語言的設計與演化》的14.2.8節中進行了深入的討論。

“Class”class

眾所周知Java有個Object class,是所有Java classes的繼承根源,其內聲明瞭數個應該在所

有Java class中被改寫的methods:hashCode()、equals()、clone()、toString()、

getClass()等。其中getClass()返回一個Class object。

Class class十分特殊。它和一般classes一樣繼承自Object,其實體用以表達Java程式執行時

的classes和interfaces,也用來表達enum、array、primitive Java types

(boolean, byte, char, short, int, long, float, double)以及關鍵詞void。當一

個class被載入,或當載入器(class loader)的defineClass()被JVM呼叫,JVM 便自動產

生一個Class object。如果您想借由“修改Java標準庫原始碼”來觀察Class object的實際生成

時機(例如在Class的constructor內新增一個println()),不能夠!因為Class並沒有

public constructor。

Class是Reflection故事起源。針對任何您想探勘的class,唯有先為它產生一個Class

object,接下來才能經由後者喚起為數十多個的Reflection APIs。這些APIs將在稍後的探險

活動中一一亮相。

#001 public final

#002 class Class<T> implements java.io.Serializable,

#003 java.lang.reflect.GenericDeclaration,

#004 java.lang.reflect.Type,

#005 java.lang.reflect.AnnotatedElement {

#006 private Class() {}

#007 public String toString() {

#008 return ( isInterface() ? "interface " :

#009 (isPrimitive() ? "" : "class "))

#010 + getName();

#011 }

...

圖1:Class class片段。注意它的private empty ctor,意指不允許任何人經由程式設計方式產生Class object。是的,其object 只能由

JVM 產生。

“Class” object的取得途徑

Java允許我們從多種管道為一個class生成對應的Class object。圖2是一份整理。

Class object 誕生管道示例

運用getClass()

注:每個class 都有此函式

String str = "abc";

Class c1 = str.getClass();

運用 Class.getSuperclass()

Button b = new Button();

Class c1 = b.getClass();

Class c2 = c1.getSuperclass();

運用static methodClass.forName()

(最常被使用)

Class c1 = Class.forName ("java.lang.

String");

Class c2 = Class.forName ("java.awt.Button");

Class c3 = Class.forName ("java.util.

LinkedList$Entry");

Class c4 = Class.forName ("I");

Class c5 = Class.forName ("[I");

運用 .class 語法

Class c1 = String.class;

Class c2 = java.awt.Button.class;

Class c3 = Main.InnerClass.class;

Class c4 = int.class;

Class c5 = int[].class;

Class.forName(xxx.xx.xx) 返回的是一個類, .newInstance() 後才建立一個物件 Class.forName(xxx.xx.xx);的作用是要求JVM查詢並載入指定的類,也就是說JVM會執行該類的靜態程式碼段

Class aClass = Class.forName(xxx.xx.xx);
Object anInstance = aClass.newInstance();


Class.forName("").newInstance()返回的是object

但是建立例項時有個限制,你的class建構函式不能包括引數,而且你需要自己來手工cast例項

用時:例項:

  protected final static String CLASS_TEST = "Test";

 protected final static String CLASS_PROBLEM = "Problem";

 protected final static String CLASS_PARAMETER = "Parameter";

 getProblem(vx, vy){

Object      result;

        result = Class.forName(CLASS_PROBLEM).newInstance();

........

 } 

 getParameters(){

        Object      result;

        result = Class.forName(CLASS_PARAMETER).newInstance();

........

 }

 buildTest(){

   String error_msg = (String) invokeMethod(

        Class.forName(CLASS_TEST).newInstance(), 

        "add", 

        new Class[]{

          Class.forName(CLASS_PROBLEM), 

          Class.forName(CLASS_PARAMETER)},

        new Object[]{

          getProblem(vx, vy), 

          getParameters()});

 } 

 protected Object invokeMethod(Object o, String name, Class[] paramClasses, Object[] paramValues) {

    Method      m;

    Object      result;

    result = null;

    try {

      m      = o.getClass().getMethod(name, paramClasses);

      result = m.invoke(o, paramValues);

    }

    catch (Exception e) {

      e.printStackTrace();

      result = null;

    }    

    return result;

  }

class Test{

public static model train_model(problem prob, parameter param)

}

class Problem{

...........

}

Parameter{

...............

}

附:

大家或許有些疑問,jdbc連線資料庫時,有的寫法是Class.forName(xxx.xx.xx);而有一些:Class.forName(xxx.xx.xx).newInstance(),為什麼會有這兩種寫法呢?


Class.forName(xxx.xx.xx) 返回的是一個類,
.newInstance() 後才建立一個物件

Class.forName(xxx.xx.xx);的作用是要求JVM查詢並載入指定的類,也就是說JVM會執行該類的靜態程式碼段

在JDBC規範中明確要求這個Driver類必須向DriverManager註冊自己,即任何一個JDBC Driver的Driver類的程式碼都必須類似如下:
public class MyJDBCDriver implements Driver {
static {
DriverManager.registerDriver(new MyJDBCDriver());
}
}

所以我們在使用JDBC時只需要Class.forName(XXX.XXX);就可以了