簡體   English   中英

Java中的泛型 - 與遺留代碼互操作

[英]Generics in java - interoperating with legacy code

我正在從Oracle http://docs.oracle.com/javase/tutorial/extra/generics/legacy.html閱讀本教程

但我無法弄清楚這條線的含義

因此,即使存在未經檢查的警告,Java虛擬機的類型安全性和完整性也不會存在風險。

有人可以更清楚地為我解釋一下嗎? 補充:JVM的“ 完整性 ”究竟是什么?“ 有風險 ”究竟是什么意思?

這意味着JVM永遠不會被認為對象是一種它不是的類型。 欺騙運行時認為一塊數據具有不同的類型是一個強大的攻擊向量,特別是如果你可以欺騙運行時允許你為它認為是longint的值賦值,但實際上是一個指針。

JVM的基本安全模型依賴於對象是運行時認為它的類型。

我讀了一篇引人入勝的論文,詳細介紹了對運行Java的機器的攻擊,該機器涉及使用加熱燈來大幅增加內存錯誤。 然后,他們使用了一個程序,數十億個對象在內存中策略性地對齊,並等待一個有零星的位翻轉。 這樣做會欺騙JVM認為它正在處理不同類型的對象,並最終允許JVM運行任意代碼(對於完整的讀取,請參閱使用內存錯誤來攻擊虛擬機 )。

他們使用位翻轉的事實與泛型無關。 但是,該文章詳細介紹了欺騙運行時思考對象的不同類型的能力。 總而言之,想象一下你有AB類:

class A {
    public long data = 0;
}

class B {
}

如果你能以某種方式欺騙JVM允許這樣做:

A aButActuallyB = someMagicAssignment(new B());

someMagicAssignment是一個可以引用B的方法,並以某種方式將引用對象作為A返回。 后來想想會有什么實際發生時,你然后做:

aButActuallyB.data = 0x05124202;

您可能正在寫入JVM原始內存中的任意數據! 例如,該數據可以是方法的位置。 將其更改為指向某個字節數組的內容可以讓您隨后運行任意代碼。

所以當Oracle說

即使存在未經檢查的警告,Java虛擬機的類型安全性和完整性也不會存在風險。

它的含義是即使你能做到這一點:

public static <T> T someMagicAssignment(B b){ 
   return (T) b; //unchecked cast warning
}

然后調用它:

A a = MyClass.<A>someMagicAssignment(new B());

這仍然會在分配a時進行運行時檢查。

因此,編寫該方法someMagicAssignment並不比以前容易。 泛型不會以任何方式增加此攻擊向量的表面區域,因為JVM在其內部類型系統中忽略了泛型。 從來沒有JVM允許您為方法提供List<String> ,然后讓該方法對該列表的元素執行String操作,而不在運行時檢查元素實際上是String 絕不允許你在沒有人工檢查A情況下將B視為A

它只是意味着,當你的代碼中有List<String> stringListList stringList時,一旦編譯並運行,JVM就沒有風險了。 JVM只看到List stringList

這是因為類型擦除 ,其中Generics參數化類型在運行時被擦除(移除)。

同一篇文章明確指出:

基本上, 擦除消除(或擦除)所有泛型類型信息。 拋出尖括號之間的所有類型信息,例如,像List<String>這樣的參數化類型被轉換為List


文檔中的風險只是意味着,如果你有一個例子

List<String> stringList = new Arrays.asList("one", "two", "three");
String number = stringList.get(0);

JVM將其理解為:

List stringList = new Arrays.asList("one", "two", "three");
String number = (String)stringList.get(0);

雖然,第二個版本會抱怨泛型原始類型 ,在JVM中移除參數化的類型和隱式地檢索元素作為Object和JVM的類型轉換規則仍然適用。

JVM 從不存儲/使用泛型信息(即使元數據駐留在類文件中)。

我希望這有幫助。

我認為他們得到的是泛型是在編譯器中實現的,而不是在虛擬機中實現的,因此無論是否抑制未經檢查的警告,編譯器發出的字節碼都是相同的; 抑制未經檢查的警告未以繞過虛擬機級別的類型檢查的方式實現,僅在編譯器級別實現。

答案在緊接在那一句之前的句子中:

原因是,Java編譯器將泛型實現為稱為擦除的前端轉換。 您(幾乎)可以將其視為源到源的轉換,其中漏洞()的通用版本將轉換為非泛型版本。

這意味着,由於擦除,具有泛型的源代碼將編譯為與非通用源代碼相同(或至少相似)的字節碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM