new的奧秘:Java中new關鍵字與類加載器
原創【51CTO獨家特稿】在Java中使用new關鍵字創建對象變得很容易了,事實上,對這些事情你是不需要考慮的。需要訪問一個文件嗎?只需要創建一個新的File實例:new File(“build.properties”),對于大多數Java開發人員而言,這就是他們需要知道的一切,是不是很簡單呢?!但當你使用了多個類加載器時,問題就不一樣了。
這是我一年多來的第一反應,我就是不想知道這些東西,但奇怪的是,類加載器其實非常簡單,大多數Java開發人員都知道編譯時通過Java文件生成.class類文件,然后由Java虛擬機(JVM)載入這些編譯后的類,這就是類加載器最基本的功能,但是和線程一樣,問題不是理解他們做什么,而是讓它們一起工作。
你聽到過多少次“這是類加載器的問題”?我承認我聽到過很多次,我自己也說過很多次。只要你的應用程序中不止一個類加載器,你不得不擔心哪些類可以相互看到對方,這很容易成為一場噩夢。有關類加載器行為我將另外用一篇文章來說,現在我們還是回到new關鍵字吧。
當你創建一個新對象時,JVM首先加載類,當你使用new時這是透明的,問題是使用什么類加載器?以及為什么要使用它?
設想一個Grails情景,我們有一套基于Gant的構建系統,載入構建腳本并執行它們,我們以實例化一個Jetty服務器并啟動它作為示例,對象的創建順序是這樣的:
事實上,上圖展示的僅僅是一個簡化的真實情況。
前3個類都在我們將要調用的生成類加載器的類路徑下,因此是一次性將生成時需要用到的所有類全部加載了,但Jetty的Server類怎么加載呢?最重要的是要知道Server類必須要通過加載Grails Web應用程序相同的類加載器加載,雖然你可以將你自己的類加載器嵌入到服務器中,如果與加載Server的不一樣,將會出現可怕的類加載器問題。
考慮到這一點,讓我們看看如果RunApp腳本使用new創建服務器實例會發生什么:
- def server = new org.mortbay.jetty.Server()
- ...
- server.start()
現在你應該問你自己“加載Server類該使用什么類加載器?”,這是一個關鍵問題,因為它決定了使用什么類加載器加載整個Web應用程序,也就決定了應用程序的運行時應該使用和依賴的類路徑,在這種情況下,無論使用什么類加載器加載RunApp腳本,new操作符都會有效地授權給
- this.getClass().getClassLoader()
我們的例子是什么意思呢?它意味著生成類加載器被用于加載Server類,因此也必須用于加載Web應用程序類,換句話說,所有應用程序的運行時依賴必須包括在生成類加載器中。你可能會問,這樣會不會有問題?回答是有一個潛在的問題和一個實際的問題。
潛在的問題是類沖突,如果Web應用程序依賴一個已經存在于生成系統中不同版本的庫會怎么樣?如果所有Apache XML API庫都在類路徑下,這是一個特殊的問題,絕對會導致大破壞。
實際的問題是在類路徑中JAR文件越多,JVM尋找類的時間就越長,這意味著啟動時間就越長,這也是OSGi設計要解決的問題之一,為什么要將JAR放在生成類路徑下呢?生成時本身是不需要它們的。
解決辦法是確定類加載器的邊界,使用映射實例化你的對象:
- def runtimeClassLoader = new URLClassLoader(...)
- def server = runtimeClassLoader.loadClass("org.mortbay.jetty.Server").newInstance()
- ...
- server.start()