1. 程式人生 > >java之rtti(run-time Type identification)類物件

java之rtti(run-time Type identification)類物件

RTTI (run-time Type identification) :

為什麼要使用rtti:rtti代表java型別資訊,從面向物件的角度來說,每一個物件都是一個類,那麼型別也是一個類:型別類,其保證不僅在編譯期知道java物件的型別,並且可以在執行期 運用物件的型別資訊,在jvm中方法區儲存class資訊,但是型別物件一般存放在堆中 儘管物件使用了java多型中的向上轉型,依然可以識別出其型別類,見instanceof 關鍵字 java在執行時識別物件型別有兩種情況:傳統rtti反射 類是程式的一部分,每一個類都有一個class物件,java使用class物件來執行RTTI

Class物件中的forName方法

@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

通過原始碼可以看到Class的靜態方法forName 返回一個Class物件,這個方法的副作用是,如果 引數className所指向的類還沒有被jvm載入,就先載入該類,執行其中的static方法。 可以不用為了使用該Class引用而持有該型別的物件。 可以看到使用class的forName方法 執行了其中的static方法,而使用.class 獲得型別類(類字面常量,下一節會講),僅把類載入進jvm,而不執行其中的static語句

類中的static方法,在class物件第一次被載入進jvm時執行,而class物件只在需要時被載入,也就是需要訪問類中的靜態方法時,因此可以認為建構函式也是class的一個靜態方法。 深入理解java虛擬機器 中所述,虛擬機器把class檔案載入到記憶體,並進行校驗,解析初始化,最後形成可以被虛擬機器直接使用的java型別,這就是虛擬機器的類載入機制,這些都是在執行期進行的,因此是java具有良好的動態擴充套件語言特性。 其中規定,遇到new、getstatic、putstatic或invokestatic這四個位元組碼指令時,如果類沒有進行初始化,首先觸發其初始化。

載入

載入包括三個步驟: 1. 獲取位元組流 2. 將位元組流所代表的靜態儲存結構轉化為方法區執行時資料結構 3. 記憶體中生成一個代表這個類的java.lang.Class物件,做為方法區這個類的各種資料的訪問入口 * 載入完成後生成Class類的物件,並沒有規定必須在堆中,也可以在方法區中,這個物件可以訪問方法區中這些型別資料的外部介面,因此也造成多用於反射建立物件。 * 那麼什麼是方法區呢? 首先需要了解一下java虛擬機器的執行時資料區 其中綠色的是每個執行緒獨有的,程式計數器表示位元組碼的行號指示器 方法區和堆是執行緒之間共享的。其中的作用是存放物件例項,幾乎所有的物件例項及陣列都要在堆上分配。方法區儲存被虛擬機器載入的類資訊、常量、靜態變數等,執行時常量池也是方法區的一部分(Metaspace表示型別的型別,不在jvm記憶體中,而是在本地記憶體中)。

類字面常量

也就是上面說的通過 類名.class 獲得型別的引用,使用這種方式不會自動的初始化該class物件。 為使用類而做的準備工作包含三個步驟: 1. 載入 載入class,並建立一個class物件 2. 連結 分配靜態空間 3. 初始化 執行靜態初始化程式碼,或靜態程式碼塊時初始化 如果一個static final 是編譯器常量,那麼不需要類初始化就可以讀取,但是static修飾的不是一個編譯器常量(比如不是final修飾的,或者是一個隨機變數),那麼必須強制進行類初始化。

使用泛型,使 類引用更具體(利用了編譯期檢查)

instanceof

使用場景:在向下轉型前,如果沒有其他的資訊表明這是一個什麼類,那麼使用instanceof是必須的,防止castException異常

使用反射可以呼叫向上轉型後子類中的方法

也給程式留下了後門

public class ConcreteRtti implements IRttiInterface {

    @Override
  public void say() {
        System.out.println ("this is IRttiInterface");
    }
    private void concreteSay(){
        System.out.println ("this is concreteSay");
    }
    public static void main(String[] args) throws Exception {
        IRttiInterface iRttiInterface = new ConcreteRtti ();
        iRttiInterface.say ();
        System.out.println (iRttiInterface.getClass ().getName ());
        ConcreteRtti iRttiInterface1 = (ConcreteRtti) iRttiInterface;
        iRttiInterface1.concreteSay (); //呼叫非介面中的方法
        Method concreteMethod = iRttiInterface.getClass ().getDeclaredMethod ("concreteSay");
        concreteMethod.invoke (iRttiInterface);//呼叫非介面中的方法
    }
}

反射reflect

java reflect包中 包含Construct Field Method等類 反射是在執行時開啟和檢查.class檔案,而普通rtti實在編譯器 檢查和執行.class,簡單的使用

public static void main(String[] args) throws Exception {
    Class<ReflectTest> reClass = ReflectTest.class;
    ReflectTest test = reClass.newInstance (); //使用newInstance 必須有一個無參構造器
  Constructor[] cons = reClass.getConstructors ();
    Method[] methods = reClass.getMethods ();
    Method method0 = null;
    method0 = reClass.getMethod ("say");
    assert method0 != null;
    method0.invoke (test);
}

使用反射技術包括 動態代理等