RTTI


RTTI 翻譯過來是運行時類型信息。一個引用不僅可以指向和自己類型一致的對象,還可以指向自己子類的對象。那么JVM在執行代碼時是如何判定引用指向的對象是否合法?這時就需要用到RTTI。

一個小案例


class Base {}

class Foo {}

public class Test{
	public static void main(String[] args) {
		Base base = new Base();
		Object obj = base;
		Foo foo = (Foo) obj;    //運行時拋異常
	}
}

上的代碼編譯時會通過,但是運行時會拋出ClassCastException異常。那么問題來了,編譯時為啥通過了?JVM虛擬機咋就是知道強轉出現問題了?首先分析代碼

Base base = new Base();
Object obj = base;
Foo foo = (Foo) obj;

第一行創建了一個Base對象,定義一個Base引用,將該引用指向創建的對象。這行代碼是沒有什么問題,因為引用的類型和對象的類型是一致的。

第二行代碼將Base型引用賦值給一個Object引用,實質是讓obj指向base指向的對象。這是向上轉型,沒有問題,當然編譯器會對向上轉型做檢查的。

第三行代碼就出現問題了,它實質是讓一個Foo類型引用指向了一個Base對象,這是不合理的。因為從類的聲明上來看這個兩個類半毛錢關系都沒有。

分析完畢后我們來分析一下為啥編譯能通過。首先一行是沒有異議的。第二行是編譯器會查看類的繼承,它發現base是Base類型,而obj是Object類型,從繼承角度來講將子類引用賦值給父類引用是沒有問題的。第三行代碼對編譯器來說也是沒有問題了,因為無論是向上還是向下轉型,編譯器只看繼承關系,如果存在繼承關系那么編譯器會放過的,Object是所有類的基類,所以沒有毛病。編譯器其實只能做一些簡單的類型檢查,即它只判斷賦值號左邊的類型和右邊類型是否一致或存在某種關系,它不能透過現象看本質。

JVM是如何知道轉型出現了問題了?它查看了obj的RTTI(通俗的講它查看了obj指向對象的類型)發現和foo變量的類型不一致,所以它認為強轉出現了問題,實際上我們不能將Foo引用指向Base對象。

如何查看RTTI

查看一個引用的RTTI和class對象密不可分,這個對象是一個特殊對象,和類同生共死。class對象中包含了類的所有信息,比如類名。當我們編譯一個類后,編譯器會生成相應的class對象,並將該類和class對象一同寫到了.class文件中。當類加載器加載類時會連同class對象一同加載到內存。而當我們創建對象時,JVM虛擬機是根據class對象創建出普通對象。因此,普通對象中將會持有class對象的引用。現在事情就很簡單了,JVM通過引用找到普通對象,通過普通對象中的引用找到class對象,通過class對象查到了類信息,這時一個引用的RTTI就被獲得了。

獲取RTTI

RTTI用於描述class對象信息,Java中也為程序員提供了獲取RTTI的接口

Object obj = new Base();
Class<?> c = obj.getClass();  //返回class對象引用

上面的代碼我們獲取了class對象,而obj的RTTI就在c中

前面提到對象都是通過class對象創建出來的,因此我們可以采用別的方式來創建對象,下面是一個小例子:

Class<?> baseClass = Class.forName("Base"); //返回class對象引用
Base base = baseClass.newInstance();

RTTI的表現形式

以下行為會查看RTTI:

  • 向下轉型,A a = (B) b;
  • 獲取Class對象,Class<?> c = a.getClass();
  • 對於關鍵字instanceof的使用,instanceof主要用來查看一個對象是否屬於某個類

反射

要講反射首先要將RTTI和反射的應用場景搞清楚。反射和RTTI是沒有直接關系的,只不過因為反射也涉及到類型信息才放到這里講。
RTTI的應用場景是這樣的:給一個引用,然后識別這個引用的類型信息,它需要建立在類已知的基礎上,即編譯器見過這個類,那么JVM或者在我們代碼中才能獲取一個引用的RTTI。

通過反射也能獲取類型信息,它是在我們不知道類信息的基礎上來做的。比如某個類沒有在本地存儲,而是在將來的某個時刻會通過網絡發送過來。那么我們在代碼中無法通過new的方式來創建這個類的對象,也不能調用其方法(如果這樣做,編譯器會報錯說找不到該類),這時就需要用到反射了。

下面舉一個簡單的例子來查看類信息

package xdysite.cn;

import java.lang.reflect.Method;

class Base {
	public void b() {
		System.out.println("It is Base class");
	}
}

public class Test{
	public static void main(String[] args) {
		try {
			//獲取class對象
			Class<?> c = Class.forName("xdysite.cn.Base");
			//獲取內部方法b
			Method m = c.getMethod("b");
			//創建普通對象
			Object obj = c.newInstance();
			//調用該方法
			m.invoke(obj);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

注意:雖然Base和Test在同一個包內,但是要調用Base中的方法,其權限必須是public,否則會報找不到該方法

http://www.cnblogs.com/lzq198754/p/5780331.html

小結

RTTI和反射都是獲取類型信息,但是兩個的應用場景是不一致的,所以在理解的時候要分清楚這一點


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM