程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> java入門概念個人理解之集合類框架的簡要知識點:泛型的類型擦除,java框架

java入門概念個人理解之集合類框架的簡要知識點:泛型的類型擦除,java框架

編輯:JAVA綜合教程

java入門概念個人理解之集合類框架的簡要知識點:泛型的類型擦除,java框架


 

這裡想說一下在集合框架前需要理解的小知識點,也是個人的膚淺理解,不知道理解的正不正確,請大家多多指教。
這裡必須談一下java的泛型,因為它們聯系緊密,我們先看一下這幾行代碼:

 

Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println( c1 + "==" + c2 + " is " + (c1 == c2) );


這裡主要想測試一下這兩個類是不是相等的,根據我之前的認識,這應該是不相等的,但是運行輸出的結果是:

 

class java.util.ArrayList==class java.util.ArrayList is true

 

根據打印結果顯然c1 和 c2都叫class java.util.ArrayList ,所以後面是否相等跟的是 true,居然和我的預期完全相反的,
這是怎麼回事呢,這就要提到java泛型裡的類型擦除(type erasue),這黑科技的提出肯定是有多方面原因的,我摘錄了一些我了解到的原因。
讓我們一步步了解:

 

一、保持代碼版本的兼容性
在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實現參數的"任意化","任意化"帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型可以預知的情況下進行的。對於強制類型轉換錯誤的情況,編譯器可能不提示錯誤,在運行的時候才出現異常,這是一個安全隱患。所以後期引入了泛型,泛型的好處是在編譯的時候檢查類型安全,並且所有的強制轉換都是自動和隱式的,提高代碼的重用率。但是需要保持代碼的兼容性


二、目前了解到通常情況下編譯器處理泛型有兩種方式,java選擇了類型擦除,兩種方式如下:
1.Code specialization。在實例化一個泛型類或泛型方法時都產生一份新的目標代碼(字節碼or二進制代碼)。例如,針對一個泛型list,可能需要 針對string,integer,float產生三份目標代碼。
2.Code sharing。對每個泛型類只生成唯一的一份目標代碼;該泛型類的所有實例都映射到這份目標代碼上,在需要的時候執行類型檢查和類型轉換。
C++中的模板(template)是典型的Code specialization實現。C++編譯器會為每一個泛型類實例生成一份執行代碼。執行代碼中integer list和string list是兩種不同的類型。這樣會導致代碼膨脹(code bloat),
Code specialization另外一個弊端是在引用類型系統中,浪費空間,因為引用類型集合中元素本質上都是一個指針。沒必要為每個類型都產生一份執行代碼。而這也是Java編譯器中采用Code sharing方式處理泛型的主要原因。
Java編譯器通過Code sharing方式為每個泛型類型創建唯一的字節碼表示,並且將該泛型類型的實例都映射到這個唯一的字節碼表示上。將多種泛型類形實例映射到唯一的字節碼表示是通過類型擦除(type erasue)實現的。

 

類型擦除的來世大概說了,我們看看類型擦除大概是怎麼回事:
我們都知道重載,就是在類中可以創建多個方法,它們具有相同的名字,但具有不同的參數和不同的定義,根據java編輯器處理泛型的方式,上面代碼編譯完成後應該會變成這樣:

 

Class c1 = new ArrayList<Object>().getClass();
Class c2 = new ArrayList<Object>().getClass();
System.out.println( c1 + "==" + c2 + " is " + (c1 == c2) );

 

  

可以看出來,無論類是什麼,經過編譯器後再虛擬機裡運行都是相同的類,所以結果是相等的。

 

 

 

所以總結一下類型擦除引起一些奇怪的現象,包括:

 

  • 泛型類並沒有自己獨有的Class類對象。比如上面測試的例子。
  • 靜態變量是被泛型類的所有實例所共享的。比如聲明為MyClass<T>的類,不管是通過new MyClass<String>還是new MyClass<Integer>創建的對象,都是共享一個靜態變量。
  • 泛型的類型參數不能用在Java異常處理的catch語句中。因為異常處理是由JVM在運行時刻來進行的。由於類型信息被擦除,JVM是無法區分兩個異常類型MyException<String>和MyException<Integer>的。對於JVM來說,它們都是 MyException類型的。也就無法執行與異常對應的catch語句。 

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved