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語句
載入
載入包括三個步驟: 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);
}
使用反射技術包括 動態代理等