作者:Flyingis
ClassCastException是JVM在檢測(cè)到兩個(gè)類(lèi)型間轉(zhuǎn)換不兼容時(shí)引發(fā)的運(yùn)行時(shí)異常。此類(lèi)錯(cuò)誤通常會(huì)終止用戶請(qǐng)求。在執(zhí)行任何子系統(tǒng)的應(yīng)用程序代碼時(shí)都有可能發(fā)生ClassCastException異常。通過(guò)轉(zhuǎn)換,可以指示Java編譯器將給定類(lèi)型的變量作為另一種變量來(lái)處理。對(duì)基礎(chǔ)類(lèi)型和用戶定義類(lèi)型都可以轉(zhuǎn)換。Java語(yǔ)言規(guī)范定義了允許的轉(zhuǎn)換,其中大多數(shù)可在編譯時(shí)進(jìn)行驗(yàn)證。不過(guò),某些轉(zhuǎn)換還需要運(yùn)行時(shí)驗(yàn)證。如果在此運(yùn)行時(shí)驗(yàn)證過(guò)程中檢測(cè)到不兼容,JVM就會(huì)引發(fā)ClassCastException異常。例如:
Fruit f;
Apple a = (Apple)f;
當(dāng)出現(xiàn)下列情況時(shí),就會(huì)引發(fā)ClassCastException異常:
1. Fruit和Apple類(lèi)不兼容。當(dāng)應(yīng)用程序代碼嘗試將某一對(duì)象轉(zhuǎn)換為某一子類(lèi)時(shí),如果該對(duì)象并非該子類(lèi)的實(shí)例,JVM就會(huì)拋出ClassCastException異常。
2. Fruit和Apple類(lèi)兼容,但加載時(shí)使用了不同的ClassLoader。這是這種異常發(fā)生最常見(jiàn)的原因。在這里,需要了解一下什么是ClassLoader?
ClassLoader
ClassLoader是允許JVM查找和加載類(lèi)的一種Java類(lèi)。JVM有內(nèi)置的ClassLoader。不過(guò),應(yīng)用程序可以定義自定義的ClassLoader。應(yīng)用程序定義新的ClassLoader通常出于以下兩種原因:
1. 自定義和擴(kuò)展JVM加載類(lèi)的方式。例如,增加對(duì)新的類(lèi)庫(kù)(網(wǎng)絡(luò)、加密文件等)的支持。
2. 劃分JVM名稱空間,避免名稱沖突。例如,可以利用劃分技術(shù)同時(shí)運(yùn)行同一應(yīng)用程序的多個(gè)版本(基于空間的劃分)。此項(xiàng)技術(shù)在應(yīng)用服務(wù)器(如WebLogic Server)內(nèi)的另一個(gè)重要用途是啟用應(yīng)用程序熱重新部署,即在不重新啟動(dòng)JVM的情況下啟動(dòng)應(yīng)用程序的新版本(基于時(shí)間的劃分)。
ClassLoader按層級(jí)方式進(jìn)行組織。除系統(tǒng)BootClassLoader外,其它ClassLoader都必須有父ClassLoader。
在理解類(lèi)加載的時(shí)候,需要注意以下幾點(diǎn):
1. 永遠(yuǎn)無(wú)法在同一ClassLoader中重新加載類(lèi)。“熱重新部署”需要使用新的ClassLoader。每個(gè)類(lèi)對(duì)其ClassLoader的引用都是不可變的:this.getClass().getClassLoader()。
2. 在加載類(lèi)之前,ClassLoader始終會(huì)先詢問(wèn)其父ClassLoader(委托模型)。這意味著將永遠(yuǎn)無(wú)法重寫(xiě)“核心”類(lèi)。
3. 同級(jí)ClassLoader間互不了解。
4. 由不同ClassLoader加載的同一類(lèi)文件也會(huì)被視為不同的類(lèi),即便每個(gè)字節(jié)都完全相同。這是ClassCastException的一個(gè)典型原因。
5. 可以使用Thread.setContextClassLoader(a)將ClassLoader連接到線程的上下文。
基于以上的基本原理,可以加深大家對(duì)ClassCastException的理解,和在碰到問(wèn)題時(shí)提供一種解決問(wèn)題的思路。
參考文獻(xiàn):
dev2dev專(zhuān)刊 2005年 第二期
j2sdk-1_5_0-doc