1、類型時類型識別(run-timetype identification,RTTI):當之有一個指向對象的引用時,RTTI可以讓你找出這個對象的確切類型。
2、Java運行時識別對象和類的信息,主要有倆種方式:
1).一種是“傳統“RTTI,它假定我們在運行時已經知道了所有的類型。
2).另一種是“放射“機制,它允許我們在運行時獲得類的信息。
3、Class對象:每個類都有一個Class對象,保存在一個同名的.class文件中,它包含了與類相關的信息。
4、在運行時,當我們想生成這個類的對象時,運行這個程序的Java虛擬機(JVM)首先檢查這個類的Class對象是否已經加載,就會根據類名查找.class文件,並將其載入。所以Java並非一開始執行就完全加載的,這一點與許多傳統語言都不同。
5、Class.forName(“類名”)是獲得Class引用的一種方法,該方法返回一個Class對象的引用。例如:Class.forName(“MyClass”);
在調用該方法時,若MyClass類被還沒加載,則加載MyClass類。
6、類字面常量:另一種獲得Class引用的方法,MyClass.class(類名.class)。
此方法,更安全,更高效。
7、類字面常量不僅應用於普通的類,也可以應用於接口、數組以及基本數據類型。
8、RTTI的形式包括:
1).傳統的類型轉化。如Base類是Subclass類基類。
Base base = new Subclass();
Subclass sub= (Subclass)base;//(Subclass),由RTTI確定類型轉化正確性。在C++中,經典的類型轉換“(Subclass)”並不使用RTTI。
2).代表對象的類型的Class對象。同過查詢Class對象可以獲取運行時所需的信息。
3).RTTI的第三種形式,使用關鍵字“instanceof”。如:
boolean yesOrNot = (base instanceof Subclass);
若base是Subclass類的實例,yesOrNot就爲true。
9、instanceof與Class的等價性:
class Base{}
class Subclass extends Base{};
public class Test{
static void test(Object x){
System.out.println("Testing x of type: " + x.getClass());
System.out.println("x instanceof Base: " + (x instanceof Base));
System.out.println("x instanceof Subclass: " + (x instanceof Subclass));
System.out.println("Base.isInstance(x): " + (Base.class.isInstance(x)));
System.out.println("Subclass.isInstance(x):" + (Subclass.class.isInstance(x)));
System.out.println("x.getClass() == Base.class: " + (x.getClass() == Base.class));
System.out.println("x.getClass() == Subclass.class: " + (x.getClass() == Subclass.class));
System.out.println("x.getClass().equals(Base.class): " + (x.getClass().equals(Base.class)));
System.out.println("x.getClass().equals(Subclass.class): " + (x.getClass().equals(Subclass.class)));
}
public static void main(String[] args){
test(new Base());
System.out.println("---------------------------------------------------");
test(new Subclass());
}
}
運行結果:
1).instanceof 與 isInstance生成的結果完全一樣,equal與==也一樣。
2). Instanceof與isInstance保持了類型的概念,它指得是“你是這個類,或者是這個類的派生類嗎?”
3).equal與==比較實際的Class對象,沒有考慮繼承。
10、Java是通過Class對象來實現RTTI機制的,即使我們只是做些諸如類型轉換這類的事情。Class類提供的一些方法:
11、RTTI的限制:編譯器必須已經知道所有用RTTI來處理的類型。但是,當你從網絡連接中獲得一串字節,並被告知這些字節代表一個類。可是編譯器並不知道這個類的信息。這時你要使用這個類,那麼就得使用反射機制。
12、RTTI與反射之間的區別:對RTTI來書,編譯器在編譯時打開和檢查.class文件,而對於反射機制,.class文件在編譯時是不可獲取的,所以是在運行時打開和檢查.class文件的。
13、獲取關於類和對象的反射信息在java.lang.reflect庫中,它包含了Field、Method以及Constructor類。
1).Field 提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。反射的字段可能是一個類(靜態)字段或實例字段。
2). Method 提供關於類或接口上單獨某個方法(以及如何訪問該方法)的信息。所反映的方法可能是類方法或實例方法(包括抽象方法)。
3). Constructor 提供關於類的單個構造方法的信息以及對它的訪問權限。
Base類如下:
class Base{
private int number = 1;
public int otherNum = 100;
public Base(){
}
public Base(int number){
this.number = number;
}
public void func(){
number++;
}
public void otherFunc(){
otherNum--;
}
}
Test:public class Test{
public static void main(String[] args){
Constructor[] constructors = null;
Method[] methods = null;
Field[] fields = null;
try {
Class c = Class.forName("Base");
constructors = c.getConstructors();//公有構造方法。
methods = c.getMethods();//公有方法。
fields = c.getFields();//公有字段。
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
System.out.println("Base含有的公有構造方法:");
for(int i = 0; i < constructors.length; i++){
System.out.println(constructors[i].getName());
}
System.out.println("Base含有的公有方法(有些是從Oject繼承的):");
for(int i = 0; i < methods.length; i++){
System.out.println(methods[i].getName());
}
System.out.println("Base含有的公有字段:");
for(int i = 0; i < fields.length; i++){
System.out.println(fields[i].getName());
}
}
}
運行結果: