Java中執行時型別識別RTTI
在Java中,並不是所有的型別資訊都能在編譯階段明確,有一些型別資訊需要在執行時才能確定,這種機制被稱為RTTI,英文全稱為Run-Time Type Identification,即執行時型別識別,有沒有一點“知行合一”的味道?執行時型別識別主要由Class類實現。
一、class 和Class
在Java中,我們常用“class”(首字母為小寫的c)關鍵字來定義一個類,說這個類是對某一類物件的抽象。例如:
public class Author {
private String penName;
private String realName;
}
現在,我們想知道Author這個類本身的一些資訊(比如說類名),該怎麼辦呢?這時候就需要用到“Class”(首字母為大寫的C)類,該類包含了與類有關的資訊。請看程式碼:
public class RTTITest1 {
public static void main(String[] args){
Author author = new Author();
Class c1 = author.getClass();
System.out.println(c1.getName());
//輸出com.feeler.interlive.rtti.Author
}
}
當我們建立了作者物件author後,就可以通過author.getClass()
獲取author的Class物件,通過c1.getName()
可獲得wanger物件的類名。
再看如下程式碼,作者Author經過努力程式設計了作家Writer。
public class Writer extends Author {
private String honour;
}
public class RTTITest2 {
public static void main(String[] args) {
Author wanger = new Writer();
Class c1 = wanger.getClass();
System.out.println(c1.getName());
//輸出com.feeler.interlive.rtti.Writer
}
}
在上例中,即使我們將Writer的物件引用wanger向上轉型為Author,wanger的Class物件型別依然是Writer(通過輸出結果可以判定)。這也就是說,Java能夠在執行時自動識別型別的資訊,它不會因為wanger的引用型別是Author而丟失wanger真正的型別資訊(Writer)。Java是怎麼做到這一點呢?
當Java建立某個類的物件,比如Writer類物件時,Java會檢查記憶體中是否有相應的Class物件。如果記憶體中沒有相應的Class物件,那麼Java會在.class檔案中尋找Writer類的定義,並載入Writer類的Class物件。
一旦Class物件載入成功,就可以用它來建立這種型別的所有物件。這也就是說,每個物件在執行時都會有對應的Class物件,這個Class物件包含了這個物件的型別資訊。因此,我們能夠通過Class物件知道某個物件“真正”的型別,並不會因為向上轉型而丟失。
二、獲取Class物件的更多方式
在使用getClass()
方法獲取一個類的Class物件時,我們必須要先獲取這個類的物件,比如上面提到的wanger。如果我們之前沒有獲取這個類的物件,就需要用另外兩種方式來獲取類的Class物件:
//method2
Class c2 = Writer.class;
System.out.println(c2.getName());
//method3
try {
Class c3 = Class.forName("com.feeler.interlive.rtti.Writer");
System.out.println(c3.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
1)當使用.class來獲取Class物件時,不會自動地初始化該Class物件,初始化被延遲到了對靜態方法或者非final靜態域進行首次引用時才執行。這樣做不僅更簡單,而且更安全,因為它在編譯時就會受到檢查(因此不需要置於try語句塊中)。
2)Class.forName會自動地初始化該Class物件,但需要指定類名,並且需要置於try語句塊中。
三、Class豐富的方法和反射
Class類提供了豐富的方法來獲取Class的相關資訊,如下常見有
getClassLoader() #獲取類載入器
String getName() #獲取全路徑名
Package getPackage() #獲取包類
Method[] getMethods() #獲取類中的方法 反射的基礎
Field[] getFields() #獲取類中的field 反射的基礎
Constructor<?>[] getConstructors() #獲取構造器 反射的基礎
還可以利用Class類提供的newInstance()
方法來建立相應類的物件,比如:
Class c2 = Writer.class;
System.out.println(c2.getName());
try {
Writer wangsan = (Writer) c2.newInstance();
System.out.println(wangsan);
// 輸出:com.cmower.java_demo.fifteen.Writer@7852e922
} catch (InstantiationException | IllegalAccessException e1) {
e1.printStackTrace();
}
// 由於我們在建立Class物件c2時沒有使用泛型,
// 所以newInstance()返回的物件型別需要強轉為Writer。我們可以在此基礎上進行改進,示例如下:
Class<Writer> c4 = Writer.class;
System.out.println(c4.getName());
try {
Writer wangsan = c4.newInstance();
System.out.println(wangsan);
// 輸出:com.cmower.java_demo.fifteen.Writer@7852e922
} catch (InstantiationException | IllegalAccessException e1) {
e1.printStackTrace();
}
下面的例子其實涉及到了反射,Field、Method(還有例子中未提到的Constructor)都來自java.lang.reflect類庫。Class類與java.lang.reflect類庫一起對反射的概念進行了支援。
public class RTTITest3 {
public static void main(String[] args) {
Class<Writer> c4 = Writer.class;
System.out.println(c4.getName());
try {
Writer wangsan = c4.newInstance();
System.out.println(wangsan);
Field[] fields = c4.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
Method[] methods = c4.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
} catch (InstantiationException | IllegalAccessException e1) {
e1.printStackTrace();
}
}
}
有時候,我們需要從磁碟檔案或網路檔案中讀取一串位元組碼,並把它轉換成一個類,這時候就需要用到反射。最常見的典型例子就是將一串JSON字串(在網路傳輸中最初的形態可能是位元組陣列)反射為對應型別的物件。
阿里巴巴提供的FastJSON提供了 toJSONString() 和 parseObject() 方法來將 Java 物件與 JSON 相互轉換。呼叫toJSONString方法即可將物件轉換成 JSON 字串,parseObject 方法則反過來將 JSON 字串轉換成物件。FastJSON的內部其實用的就是反射機制
package com.cmower.common.util;
import java.io.UnsupportedEncodingException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.alibaba.fastjson.JSON;
@SuppressWarnings("all")
public class JsonUtil {
private static Log logger = LogFactory.getLog("json");
public static byte[] objectToByte(Object obj) throws UnsupportedEncodingException {
String jsonStr = JSON.toJSONString(obj);
logger.debug("序列化後資料:" + jsonStr);
return jsonStr.getBytes("UTF-8");
}
public static <T> T byteToObject(byte[] data, Class<T> obj) throws UnsupportedEncodingException {
String objectString = new String(data, "UTF-8");
logger.debug("反序列化後資料 : " + objectString);
return JSON.parseObject(objectString, obj); }
public static <T> Object stringToObject(String data, Class<T> obj) throws UnsupportedEncodingException {logger.debug("反序列化後資料 : " + data); return JSON.parseObject(data, obj); } }
Reference:https://blog.csdn.net/qing_gee/article/details/8668462
相關文章
- C 語言Struct 實現執行型別識別 RTTIStruct型別
- 如何在執行時確定物件型別(RTTI)物件型別
- int型別和long long型別運算執行時間的差別型別
- Java資料型別、識別符號Java資料型別符號
- healthd中充電型別的識別型別
- Java註釋,識別符號,資料型別,型別轉換Java符號資料型別
- Java中的型別提升Java型別
- JAVA-識別符號、變數、資料型別Java符號變數資料型別
- JAVA中基本資料型別和引用資料型別Java資料型別
- Java中將時間戳轉化為Date型別Java時間戳型別
- JAVA 基本型別與 引用型別區別Java型別
- MySql與Java的時間型別MySqlJava型別
- Java中的資料型別Java資料型別
- java中的列舉型別Java型別
- java 中的檔案型別Java型別
- java中的型別轉換Java型別
- go資料型別識別Go資料型別
- java基礎知識--資料型別Java資料型別
- Java中基本資料型別和包裝型別有什麼區別?Java資料型別
- Java中的基本資料型別與引用資料型別Java資料型別
- Java中啟動執行緒的方式及區別Java執行緒
- Java執行緒中yield與join方法的區別Java執行緒
- swift值型別的執行緒安全Swift型別執行緒
- 執行迴路RunLoop型別機制OOP型別
- java中泛型之型別萬用字元(?)Java泛型型別字元
- Java中建立泛型型別的例項Java泛型型別
- Java的基本型別和引用型別Java型別
- java中判斷Object物件型別JavaObject物件型別
- Java中的Type型別詳解Java型別
- Java中的四種引用型別Java型別
- 識別低效執行的SQL語句SQL
- 時間型別型別
- 字元型別轉換成時間型別字元型別
- [CLR via C#]4. 型別基礎及型別、物件、棧和堆執行時的相互聯絡C#型別物件
- 怎樣理解和識別 Linux 中的檔案型別Linux型別
- java的型別Java型別
- Oracle資料型別對應Java型別Oracle資料型別Java
- Java資料型別及型別轉換Java資料型別