3 回答

TA貢獻2036條經驗 獲得超8個贊
有2種類型的轉換:
隱式轉換,當您將類型從類型轉換為更寬泛的類型時,它是自動完成的,并且沒有開銷:
String s = "Cast";
Object o = s; // implicit casting
從較寬的類型轉換為較窄的類型時,顯式轉換。對于這種情況,您必須顯式使用如下所示的強制轉換:
Object o = someObject;
String s = (String) o; // explicit casting
在第二種情況下,運行時會產生開銷,因為必須檢查這兩種類型,并且在強制轉換不可行的情況下,JVM必須拋出ClassCastException。
摘自JavaWorld:鑄造成本
鑄造用類型之間的轉換-特別是引用類型之間,鑄造操作中,我們有興趣在這里的類型。
上位操作(在Java語言規范中也稱為擴展轉換)將子類引用轉換為祖先類引用。這種轉換操作通常是自動的,因為它總是安全的,并且可以由編譯器直接實現。
向下轉換操作(在Java語言規范中也稱為縮小轉換)將祖先類引用轉換為子類引用。由于Java要求在運行時檢查強制轉換以確保其有效,因此此強制轉換操作會產生執行開銷。如果引用的對象不是強制轉換的目標類型的實例或該類型的子類的實例,則不允許嘗試進行強制轉換,并且必須拋出java.lang.ClassCastException。

TA貢獻1824條經驗 獲得超5個贊
對于Java的合理實現:
每個對象都有一個標頭,除其他外,標頭包含一個指向運行時類型的指針(例如Double
或String
,但永遠不能是CharSequence
或AbstractList
)。假設運行時編譯器(在Sun情況下通常為HotSpot)無法靜態確定類型,則生成的機器代碼需要執行一些檢查。
首先,需要讀取指向運行時類型的指針。無論如何,這對于在類似情況下調用虛擬方法是必需的。
對于強制轉換為類類型,在您命中之前java.lang.Object
,確切知道有多少個超類,因此可以從類型指針(實際上是HotSpot中的前八個)以恒定的偏移量讀取類型。同樣,這類似于讀取虛擬方法的方法指針。
然后,讀取值僅需要與預期的強制類型轉換進行比較。根據指令集的體系結構,另一條指令將需要在錯誤的分支上分支(或出錯)。諸如32位ARM之類的ISA具有條件指令,并且可能能夠使悲傷路徑通過快樂路徑。
由于接口的多重繼承,接口更加困難。通常,對接口的最后兩個強制類型轉換將在運行時類型中進行緩存。在早期(十多年前),接口有點慢,但這已不再重要。
希望您能看到這種情況與性能無關。您的源代碼更為重要。在性能方面,您的方案受到的最大打擊可能是由于在各處跟蹤對象指針而導致的高速緩存未命中(類型信息當然很常見)。

TA貢獻1876條經驗 獲得超6個贊
例如,假設我們有一個Object []數組,其中每個元素可能具有不同的類型。但是我們始終可以肯定地知道,例如,元素0是Double,元素1是String。(我知道這是一個錯誤的設計,但是讓我們假設我必須這樣做。)
編譯器不會記錄數組中各個元素的類型。它只是檢查每個元素表達式的類型是否可分配給數組元素類型。
Java的類型信息是否仍在運行時保留?還是編譯后一切都被遺忘了,如果我們執行(Double)elements [0],我們將僅跟隨指針并將這8個字節解釋為雙精度,無論是什么?
在運行時會保留一些信息,但不會保留各個元素的靜態類型。您可以通過查看類文件格式來說明這一點。
從理論上講,JIT編譯器可以使用“轉義分析”來消除某些分配中不必要的類型檢查。但是,按照您建議的程度執行此操作將超出實際優化的范圍。分析單個元素類型的收益將太小。
此外,人們不應該這樣寫應用程序代碼。
添加回答
舉報