Java RTTI機制與反射機制

來源:互聯網
上載者:User

標籤:

1.1 什麼是RTTI?

維基百科的定義:In computer programming, RTTI (Run-Time Type Information, or Run-Time Type Identification) refers to a C++ mechanism that exposes information about an object‘s data type at runtime. Run-time type information can apply to simple data types, such as integers and characters, or to generic types. This is a C++ specialization of a more general concept called type introspection. 

運行時類型資訊使得你可以在程式運行時發現和使用類型資訊。Runtime type information (RTTI) allows you to discover and use type information while a program is running.

簡而言之:當有一個指向基類對象的引用時,使用RTTI可以查詢這個引用所引用的對象的確切類型。

在java中可以通過兩種方式讓我們在運行時識別對象和類的資訊:

(1)傳統的RTTI:它假設我們在編譯時間已經知道了所有的類型;

(2)反射機制:它允許我們在運行時發現和使用類的資訊。

 

1.2 為什麼需要RTTI?

通常需要知道某個參考型別的變數(為了利用多態的優勢,一般是泛化引用,即指向父類的引用變數)所引用的對象的具體類型, 以便根據具體的類型再做具體的不同的操作。使用RTTI可以協助我們確定泛化引用所引用對象的確切類型。

一般是如下流程:判斷引用變數的具體類型 → 轉換為具體的類型 → 執行特殊類型相關的操作。

 

1.3 Java中類型資訊在運行時是如何表示的?

這是由Class對象表示的,它包含了與類有關的資訊。Java使用Class對象來執行其RTTI,即使你正在執行的是類似轉換這樣的操作。(Java performs its RTTI using the Class object, even if you’re doing something like a cast.)

 

2.1 傳統的RTTI

嚴格的說,反射也是一種形式的RTTI,不過,一般的文檔資料中把RTTI和反射分開,因為一般的,大家認為RTTI指的是傳統的RTTI。 

Java中使用RTTI的3種形式(java中已知的RTTI形式)

(1)傳統的類型轉換

比如“(Type)”,由RTTI確保類型轉換的正確性,如果執行了一個錯誤的類型轉換,會拋出ClassCastException。在Java中,所有的類型轉換都是在運行才進行正確性檢查的。所以在運行時需要識別一個對象的類型。注意,C++中類型轉換是不會使用RTTI的。

傳統的RTTI使用轉型或instanceof形式(即以instanceof的形式或isInstance()的形式)實現,但都需要指定要轉型的類型,比如:

    public void rtti(Object obj){        Orange orange = (Orange)obj;    //其中的obj雖然是被轉型了,但在編譯期,就需要知道要轉成的類型Orange,也就是需要Orange的.class檔案        //Class clazz = Class.forName("rtti.Orange");    //相對的,反射完全在運行時在通過Class類來確定類型,不需要提前載入Orange的.class檔案。    }

 

(2)代表物件類型的Class對象

通過查詢Class對象可以擷取運行時所需的資訊。(用了Class對象,不代表就是反射,如果只是用Class對象cast成指定的類,那就還是傳統的RTTI)。其實反射和RTTI並沒有什麼本質的區別,因為java的類都是在運行是載入並解析的,而且兩者通過Class對象來擷取類型資訊。只是RTTI能夠維護的類型都是編譯時間已知的類型,而反射可以使用一些在編譯時間完全不可知的類型。

        Class clazz = Orange.class;    //擷取Class對象,Orange這個類必須是編譯時間可知的        System.out.println(clazz.getName());    //利用class對應擷取各種類型資訊        System.out.println(clazz.getTypeName());        System.out.println(clazz.getSuperclass());        System.out.println(clazz.isInterface());

 

(3)instanceof或Class.isInstance()

傳統的RTTI使用轉型或instanceof形式(即以instanceof的形式或isInstance()的形式)實現,但都需要指定要轉型的類型,比如:

        if(x instanceof Apple){    //Apple這個類必須是編譯時間可知的,編譯器在編譯時間會去開啟和檢查Apple對應的.class檔案            ((Apple)x).info();                        }                x.getClass().isInstance(apple);//聲明apple對象的類必須是編譯時間可知的

 

2.2 RTTI與反射的區別

使用RTTI必須滿足一個條件:如果想知道泛化引用的確切類型,那麼這個類在編譯時間必須可知。

 

通過RTTI可以查詢到某個對象的確切類型,這個類型在編譯時間必須已知(要麼是系統類別庫中的類,要麼是你自己在代碼中定義的類,因為一個Class定義最終會轉換成一個.class檔案,所以編譯器會去尋找看有沒有對應的.class檔案,沒有的話在編譯時間就報錯“錯誤:找不到符號”,這個過程是在編譯階段完成的),這樣才能使用RTTI識別它。換句話說,在編譯時間,編譯器必須知道所有要通過RTTI來處理的類。

 

RTTI與反射的區別如下:

RTTI:由編譯器在編譯時間開啟和檢查*.class檔案。

反射機制:由JVM在運行時開啟和檢查*.class檔案。

 

            Class clazz1 = Class.forName("rtti.Apple");    //在編譯階段編譯器不會檢查和開啟字串"rtti.Apple"指定的類對應的.class檔案,而是在運行期間由JVM載入。            System.out.println(clazz1.getName());    
        Fruit fruit = new Bnana();    //編譯階段編譯器會去尋找Fruit類和Apple類各自對應的.class檔案。        Class clazz2 = fruit.getClass();        System.out.println(clazz2.getName());

 

疑問:編譯器在編譯時間開啟和檢查*.class檔案的這一過程中具體都幹了什嗎?

 

3.1 反射機制

維基百科的定義:In computer science, reflection is the ability of a computer program to examine (see type introspection) and modify the structure and behavior (specifically the values, meta-data, properties and functions) of the program at runtime.

 

並不是所有的類型都能在編譯的時候確定下來(比如有輸入一個類名顯示出這個類的所有方法的程式),所以得把開啟和檢查.class檔案的工作推到運行時再處理。典型的情境是“基於構件的編程”和遠程方法調用(RMI) 。

 

Class.forName()產生的結果在編譯時間是不可知的,也就是說,編譯時間不會去驗證參數字串對應的類是否存在。這都是在啟動並執行時候由JVM處理的,所以這個方法會拋出ClassNotFoundException。

 

Class類支援反射,是在java.lang.reflect中包含了Field/Method/Constructor類,每個類都實現了Member介面。這些類型的對象都是由JVM在運行時建立的,用來表示未知類裡對應的成員。如可以用Constructor類建立新的對象,用get()和set()方法讀取和修改與Field對象關聯的欄位,用invoke()方法調用與Method對象關聯的方法。同時,還可以調用getFields()、getMethods()、getConstructors()等方法來返回表示欄位、方法以及構造器的對象數組。這樣,未知的對象的類資訊在運行時就能被完全確定下來,而在編譯時間不需要知道任何資訊。

 

3.2 如何擷取Class對象

(1)Class.forName(String packageNamePlusClassName);

(2)Object.getClass();

(3)類字面常量

 

3.3泛化的Class引用

(1)萬用字元方式

Class<?> intClass  = int.class;

(2)extends實現範圍限定

Class<? extends NUmber> bounded = int.class;

(3)super方式

Class<Apple> apClass = Apple.class;Apple apple = apClass.newInstance();Class<? super Fruit> up = apClass.getSuperclass();

 

3.4 instanceof與class的等價性

(1)instanceof 與 isInstance()產生的結果完全一樣,equal與==也一樣。

(2)Instanceof與isInstance()保持了類型的概念,它指的是“你是這個類,或者是這個類的衍生類別嗎?”

(3)equal與==比較實際的Class對象,沒有考慮繼承——它或者是這個確切的類型,或者不是。

 

4.總結

JAVA編程思想,第14章末尾的總結一節的概括:

第1段的意思,使用RTTI可以發現引用變數的確切類型,然後根據引用變數具體的確切類型執行對應的操作(類似switch語句,P327的例子)。這種方式就沒有使用多態,失去了多態的價值。所以盡量使用多態機制,只是在必需的時候才使用RTTI。

第2-4段,概述了RTTI在部分情境下的優勢,分別舉例說明使用RTTI可以解決的問題。

 

參考資料

JAVA編程思想,第14章,第8章 8.5.2

http://www.cnblogs.com/zhguang/p/3091378.htm

http://blog.csdn.net/cannel_2020/article/details/7226108

http://blog.csdn.net/a81895898/article/details/8457623

http://blog.sina.com.cn/s/blog_548c8a8301013j6u.html

 

Java RTTI機制與反射機制

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.