運行時類型信息使得你能夠在程序運行時發現和使用類型信息。——《Think in java 4th》
****html一般咱們在面向對象的程序設計中咱們常用多態特性使得大部分代碼儘量地少了解對象的具體類型,而是隻與對象家族中的一個通用表示打交道,這樣代碼會更容易寫,更容易讀,且便於維護,設計也更容易實現、理解和改變。因此「多態」是面向對象編程的基本目標。可是,有些時候可以知道某個泛化引用對確切類型,就可使用最簡單的方式去解決它,或者咱們必須去了解其確切功能和隱藏部分去完成某個功能時,使用RTTI和反射機制能夠幫助咱們去完成咱們想要實現的功能。
*****java
類是程序的一部分,每一個類都有一個Class對象。爲了生成這個類的對象,jvm使用了被稱爲「類加載器」的子系統。類加載器子系統實際上包含一條類加載器鏈,可是隻有一個原生類加載器,它是jvm實現的一部分(native)。若是你有特殊需求,也能夠添加額外對自定義類加載器。
全部的類都是在對其第一次使用時,動態加載到jvm中的。當程序建立第一個對類的靜態成員的引用時,就會加載這個類。這個證實構造器也是類的靜態方法 (引用自java編程思想第四版P315) ,即便在構造器以前並無使用static關鍵字。所以,使用new操做符建立類的對象也會被當作對類的靜態成員引用。
所以,Java程序在它開始運行以前並不是被徹底加載,其各個部分是在必需時才加載的。這一點與許多傳統語言都不一樣,動態加載使能對行爲,在諸如c++這樣的靜態加載語言中是很難或者根本不可能複製的。
一旦一個類的Class對象被載入內存,它就被用來建立這個類的全部對象。c++
① Class.forName("全類名");
這個方法是Class類的一個static成員,Class對象就和其餘對象同樣,咱們能夠獲取並操做它的引用(這就是類加載器的工做),forName()是取得引用的一種方法。它會使用調用類的類加載器加載指定的類。若是找不到目標類則會拋出ClassNotFoundException。使用此方法,你不須要爲了得到Class引用而持有該類型的對象。編程
② 對象.getClass();
若是你已經有了一個想要的類型的對象,那就能夠經過調用getClass()方法來獲取Class引用了,這個方法屬於Object類的成員方法,它將返回表示該對象實際類型的Class引用。若是你有一個Class對象,還可使用getInterfaces()、getSuperclass()等方法獲取接口類Class對象和基類Class對象,isInterface()方法判斷是否爲接口。數組
③ 類字面常量 類.class
Java還提供了另外一種方法來生成對Class對象的引用(例:Object.class),相比於Class.forName()不只更簡單、並且更安全,由於它在編譯器就會受到檢查(所以不須要置於try語句塊中),根除了對forName()方法的調用,因此也更高效。類字面常量不只能夠應用於普通的類,也能夠應用於接口、數組以及基本數據類型。另外,對於基本數據類型的包裝類,還有一個標準字段TYPE。TYPE字段是一個引用,指向對應的基本數據類型的Class對象:
Java編程思想中建議使用.class的形式,以保持與普通類的一致性。
有趣的是,使用.class來建立對Class對象的引用時,不會自動初始化該Class對象。
*****
爲了使用類而作的準備工做實際包含三個步驟:
僅使用.class語法來得到對類的引用不會引起初始化,可是使用Class.forName()當即就進行了初始化。若是一個static final值是「編譯期常量」,那麼這個值不須要進行初始化就能夠被讀取。安全
定義一個Class對象的引用能夠任意地指向任何Class對象網絡
Class intClass = int.class; intClass = double.class;
然而若是你操做有誤(將本應指向int.class的引用指向了double.class),只有在運行時纔可能發現錯誤的賦值,由於編譯器不知道那你的意圖,不會報錯。Java SE5提供了Class泛型,對Class引用指向的Class對象進行了限定。jvm
Class<Integer> intClass = int.class; //intClass = double.class;//編譯期報錯
使用Class泛型的目的是讓編譯期進行類型檢查,誤操做時編譯期報錯,及時發現錯誤。
通配符?表示任何類型測試
Class<?> intClass = int.class; intClass = double.class;
Class<?>與Class使用效果是等價的,可是Class<?>有不一樣的表示意義,Class<?>表示我就是選擇了不具體的類型。ui
Integer是Number子類,但Integer的Class對象 不是 Number的Class對象 的子類
//class<Number> numberClass = int.class;//不合法
使用Class<? extends Number>表示一個範圍,限定Class爲Number子類的Class對象
class<? extends Number> numberClass = int.class;//OK
使用泛型的Class引用.newInstance(),返回的是確切類型的實例對象,而不是Object。
除了相似Class<? super FancyToy>的引用調用newInstance(),返回Object類型的實例。
Class<? super FancyToy>表示「某個類,是FancyToy的超類」,是含糊的,因此返回Object類型的實例。
class Toy{} class FancyToy extends Toy{} public class GenericClass { public static void main(String[] args) throws InstantiationException, IllegalAccessException { Class<? super FancyToy> up = Toy.class; Object obj = up.newInstance();//返回Object Class<Toy> toyClass = Toy.class; Toy toy = toyClass.newInstance();//返回Toy } }
Java SE5還添加了用於Class引用的轉型語法,即cast()方法:
class Building {} class House extends Building {} public class ClassCasts { public static void main(String[] args){ Building b = new House(); Class<House> houseType = House.class; House h = houseType.cast(b); // 等價操做 h = (House)b; // 等價操做 } }
cast() 方法接受參數對象,並將其轉型爲Class引用的類型,新的轉型語法對於沒法使用普通轉型的狀況顯得很是有用,在你編寫泛型代碼時,若是你存儲了Class引用,並但願之後經過這個引用來執行轉型,這種狀況就能夠解決。
在javaSE5中另外一個新特性就是 Class.asSubclass() ,該方法容許你將一個類對象轉型爲更具體的類型,它將一個類轉換成另一個的實例,若是轉換異常就會拋出ClassCastException異常,也就是這個類不是另一個類的實例;因此咱們能夠經過它拋出異常的方式來判斷一個類是不是另一個類的實例。(java編程思想中說這是個沒什麼用的特性) Class.asSubclass淺談
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Teacher { public static void main(String[] args) { Class<?> clazz = B.class; String name = clazz.getName(); try { Class classA = clazz.asSubclass(A.class); } catch (ClassCastException e) { System.out.println(name+"不是類StopThread的子類"); } } }
在對對象進行轉型的時候須要先進行類型檢查,不然可能會拋出ClassCastException
① instanceof 關鍵字
對 instanceof 有比較嚴格的限制:只能夠將其與命名類型進行比較,而不能與Class對象做比較。
A a = new B(); if(a instanceof B) { B b = (B)a; }
須要說明的是 instanceof 關鍵字檢查的是is-a
關係,也就是能夠判斷繼承關係,a instanceof Object總爲true,而直接getClass()並進行相等(==、equals 結果一致)比較只能比較確切類型,而且不一樣類加載器加載的同一個類的Class是不一樣的。
② Class.isInstance() 方法
Class.isInstance() 方法提供了一種動態地測試對象的途徑。
A a = new B(); if( B.class.isInstance(a) ) { B b = (B)a; } // 與 instanceof 的結果一致
可以完成一些使用 instanceof 關鍵字 沒法完成的效果。
若是不知道某個對象的確切類型,RTTI能夠告訴你,但有一個限制:這個類型在編譯時必須已知,這樣才能使用RTTI識別它,並利用這些信息作一些有用的事。換句話說,在編譯時,編譯器必須知道全部要經過RTTI來處理的類。
在傳統的編程環境中不太可能出現這種狀況。但當咱們置身於更大規模的編程世界中,在許多重要狀況下咱們可能須要獲取一個指向某個並不在你的程序空間中的對象的引用,事實上,在編譯時你的程序根本無法獲知這個對象所屬的類,好比從磁盤的某個文件中,或者網絡鏈接中獲取了一串字節,而且被告知這些字節表明了一個類,那麼怎樣才能使用這樣的類呢?
Class類與java.lang.reflect類庫一塊兒對反射的概念進行了支持,該類庫包含了Field、Method以及Construtor類(每一個類都實現了Member接口)。這些類型的對象是由JVM在運行時建立的,用以表示未知類裏對應的成員。這樣你就可使用Constructor建立新的對象,用get()和set()方法讀取和修改與Field對象相關聯的字段,用invoke()方法調用與Method對象相關聯的方法。另外,還能夠調用getFields()、getMethods()和getConstructors()等很便利的方法,以返回表示字段、方法以及構造器的對象數組(詳情可查找Class類的JDK文檔 )。
Java高級特性——反射(詳細方法):
https://www.jianshu.com/p/9be58ee20dee
// 使用反射展現類的全部方法, 即便方法是在基類中定義的 package typeinfo; // Print類的print方法等價於System.Out.Println,方便減小代碼量 import static xyz.util.Print.*; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.regex.Pattern; // {Args: typeinfo.ShowMethods} public class ShowMethods { private static String usage = "usage:\n" + "ShowMethods qualified.class.name\n" + "To show all methods in class or:\n" + "ShowMethods qualified.class.name word\n" + "To search for methods involving 'word'"; // 去掉類名前面的包名 private static Pattern p = Pattern.compile("\\w+\\."); public static void main(String[] args) { if (args.length < 1) { print(usage); System.exit(0); } int lines = 0; try { Class<?> c = Class.forName(args[0]); // 反射得到對象c所屬類的方法 Method[] methods = c.getMethods(); // 反射得到對象c所屬類的構造 Constructor[] ctors = c.getConstructors(); if (args.length == 1) { for (Method method : methods) print(p.matcher(method.toString()).replaceAll("")); for (Constructor ctor : ctors) print(p.matcher(ctor.toString()).replaceAll("")); } } catch (ClassNotFoundException e) { print("No such class: " + e); } } /* public static void main(String[]) public final void wait() throws InterruptedException public final void wait(long,int) throws InterruptedException public final native void wait(long) throws InterruptedException public boolean equals(Object) public String toString() public native int hashCode() public final native Class getClass() public final native void notify() public final native void notifyAll() public ShowMethods() */ }
Java 反射獲取私有方法:http://www.javashuo.com/article/p-trszpppu-do.html
一般咱們建立一個類時,它的私有方法在類外是不可見的,可是能夠經過反射機制來獲取調用。
Method的setAccessible()方法能夠繞過私有的安全檢查,因此咱們就能夠調用類的私有方法。
method.setAccessible(true);//獲取私有權限 field.setAccessible(true);
參考文章:https://blog.csdn.net/gd_hacker/article/details/80272159
****