《java深度历险》学习笔记--深入类别载入器

1、什么是程序的动态性?
程序的动态性是指程序的class文件可以动态加载,这样做可以增加程序的弹性,节省内容,但是要以牺牲加载时间为代价,最重要的是它可以使程序在无需重启进行重新编译的情况下,更新程序功能。

 

2、程序动态性带来的好处

有了動態性,我們的應用程式就可以在不用全盤重新編譯的情況下更新系
統,或者在不用停止主程式運作的情況下(尤其是您的系統必須24 小時運轉,一
停止就會造成巨大損失時),除去系統中原有的bug,或者是增加原本不具備的
新功能。

 

3、java在动态性上和其他语言有什么不同?
c与c++本身不具有动态,它们的动态性需要借助操作系统提供的相关接口来实现,这就意味着程序要编写与本身业务逻辑无关的实现动态性的代码,这对于程序的维护不利;而java则是天生

具有动态性的语言,只是作为java程序员很少意识到动态性的存在,但是在java程序员想控制程序的动态性时,了解java语言动态性的内部实现机理是很有必要的。

 

4、java的程序动态性内部实现机理
學習 Java 的朋友一定都知道,Java 是一種天生就具有動態連結能力的技
術。Java 把每個類別的宣告、介面的宣告,在編譯器處理之後,全部變成一個
個小的執行單位(類別檔, .class),一旦我們指定一個具有public static void
main(String args[])方法的類別作為起點開始運作之後,Java 虛擬機器會找
出所有在執行時期需要的執行單位,並將他們載入記憶體之中,彼此互相交互運
作。儘管本質上是一堆類別檔,但是在記憶體之中,變成了一個”邏輯上”為一體
的Java 應用程式。所以,嚴格的來說,每個類別檔對Java 虛擬機器來說,都
是一個獨立的動態聯結函式庫,只不過它的副檔名不是 .dll 或 .so,而是 .class
罷了。因為這種特性,所以我們可以在不重新編譯其他Java 程式碼的情況下,
只修改有問題的執行單位,並放入檔案系統之中,等到下次該Java 虛擬機器重
新啟動時,這個邏輯上的Java 應用程式就會因為載入了新修改的 .class 檔,
自己的功能也做了更新。這是一個最基本的動態性功能。

 

 5、類別載入器的功用

就是把類別從靜態的硬碟裡(.class 檔),複製一份
放到記憶體之中,並做一些初始化的工作,讓這個類別”活起來”,其他人就能夠
使用它的功能。

 

6、为什么要掌握程序的动态性?
接下來眼尖的朋友就會問了:「慢著,前面說新的類別必須要等到”下次該
Java 虛擬機器重新啟動時”才會重新載入,可是有些Web Server 或EJB
Container 本身就是用Java 所撰寫,他們也是在Java 虛擬機器上面執行,那
麼,他們如何在Java 虛擬機器不重新啟動的情況下具有Hot Deployment 的
功能? 好吧!就算可以做到讀取新版本的功能好了,在不關閉Java 虛擬機器的
情況下,類別所佔用的記憶體將無法釋放,那麼記憶體裡頭肯定充滿了同一個類
別的新舊版本,如果Hot Deployment 的次數太多,記憶體不會爆掉嗎?」。這
是一個非常好的問題,也是本章之所以存在的理由。

--本质上是通过类别载入器实现的,通过增加程序的弹性,来实现无需重启就进行功能的更新。

 

7、什么是类别载入器,它的主要作用是什么?
类别载入器也是jre的重要组成部分,他的主要作用是将硬盘中的.class文件拷贝一份到内存中,使之运行。

 

8、类别载入器的运行机理
当用户在执行 java **.java命令时,java.exe会利用几个基本原则找到jre,然后把**.java对应的.class文件放到jre上执行,这样java.exe的作用就结束了,但是在这个过程中类别载入器

还实现了以下两个功能,一将对应jre下的基础类别函数库rt.jar载入内存中这叫预先载入,二将**.java对应的.class文件及相关的.class文件载入到内存中,这叫按需载入。

像基礎類別函式庫這樣的載入方法我們叫做預先載入(pre-loading),這是
因為基礎類別函式庫裡頭的類別大多是Java 程式執行時所必備的類別,所以為
了不要老是做浪費時間的I/O 動作(讀取檔案系統,然後將類別檔載入記憶體之
中),預先載入這些類別會讓Java 應用程式在執行時速度稍微快一些。相對來
說,我們自己所撰寫的類別之載入方式,叫做依需求載入(load-on-demand),
也就是Java 程式真正用到該類別的時候,才真的把類別檔從檔案系統之中載入
記憶體。,只有單獨宣告(如: B 類別)而已,是不會促使類別載入器幫
我們載入類別的,只有實體化指令(new XXX())才會讓類別載入器幫我們載入該
類別。總的來說,就彈性上和速度上的考量,如此的設計所帶來的優點(彈性和省
記憶體)遠超過額外載入時間的花費(只有第一次用到時),因此依需求載入的設計
是明智的選擇。

 

9、按需载入时类别载入器的载入原则
类别载入器的载入原则是只有实例化了的类的.class文件才会被载入,程序员可以利用这个原理来节省内存的开销。

 

10、按需载入的优缺点
按需载入的优点是可以节省内存空间的使用,缺点是需要更多的加载时间,但是这个加载时间也只有第一次加入时,因为载入后保留到内存中,即可随时使用无需重复载入,直到java虚拟机

关闭。它的本质是以时间换空间。

 

 11、隱式的(implicit)方法我們已經談過了,也就是當程式設計師用到new 這個
Java 關鍵字時,會讓類別載入器依需求載入您所需要的類別,這種方式使用了
隱式的(implicit)方法,筆者在前面提到『一般使用Java 程式語言來開發應用程
式的工程師眼中,很少有機會能夠察覺Java 因為具備了動態性之後所帶來的優
點和特性,甚至根本不曾利用過這個Java 先天就具有的特性。這不是我們的
錯,而是因為這個動態的本質被巧妙地隱藏起來,使得使用Java 的程式設計師
在不知不覺中用到了動態性而不自知』就是因為如此。但是,隱式的(implicit)
方法仍有其限制,無法達成更多的彈性,遇到這種情況,我們就必須動用顯式的
(explicit)方法來完成。顯式的方法,又分成兩種方式,一種是藉由java.lang.Class 裡的forName()
方法,另一種則是藉由java.lang.ClassLoader 裡的loadClass()方法。

 

12、「那麼,有沒有更好的方法,可以在不
修改主程式的情況下增加主程式的功能?」有的,使用顯式的方法所達成的動態
性,可以增加程式的彈性,並達成我們不希望修改主程式的需求

 

檔案:Assemblyjava
public interface Assembly
{
public void start() ;
}
檔案:Office.java
public class Office
{
public static void main(String args[]) throws Exception
{
Class c = Class.forName(args[0]) ;
Object o = c.newInstance() ;
Assembly a = (Assembly) o ;
a.start() ;
}
}
檔案:Word.java
public class Word implements Assembly
{
public void start()
{
System.out.println("Word starts") ;
}
}
檔案:Excel.java
public class Excel implements Assembly
{
public void start()
11
{
System.out.println("Excel starts") ;
}
}

 

13、,類別載入器會依繼承體系最上層的類別往下依序載入,直到所有的祖
先類別都載入了,才輪到自己載入

 

14、public static Class forName(String className)--forName0(className, true, ClassLoader.getCallerClassLoader());

public static Class forName(String name, boolean initialize,
ClassLoader loader)--forName0(name, initialize, loader);

從這裡我們可以知道,forName()方法的第三個參數,是用來指定載入類別的類
別載入器的, 只有一個參數的forName() 方法, 由於在內部使用了
ClassLoader.getCallerClassLoader()來取得載入呼叫他的類別所使用的類別
載入器, 和我們自己寫的程式有相同的效用。

使用 true 和false 會造成不同的輸出結果,您看出端倪了嗎? 過去在很多
Java 的書本上提到靜態初始化區塊(static initialization block)時,都會說「靜
態初始化區塊是在類別第一次載入的時候才會被呼叫那僅僅一次。」可是從上
面的輸出,卻發現即使類別被載入了,其靜態初始化區塊也沒有被呼叫,而是在
第一次叫用newInstance()方法時,靜態初始化區塊才真正被叫用。所以嚴格
來說,應該改成「靜態初始化區塊是在類別第一次被實體化的時候才會被呼叫那
僅僅一次。」
所以,我們得到以下結論:不管您使用的是new 來產生某類別的實體、或是使用
只有一個參數的forName()方法,內部都隱含了”載入類別+呼叫靜態初始化區
塊”的動作。而使用具有三個參數的forName()方法時,如果第二個參數給定的
是false,那麼就只會命令類別載入器載入該類別,但不會叫用其靜態初始化區
塊,只有等到整個程式第一次實體化某個類別時,靜態初始化區塊才會被叫用

 true在载入类别时初始化静态区块

false在第一次实例化类别时初始化静态区块

 

15、了解了類別載入器的來龍去脈,您將可以讓您的程式具有強大的動態性 -
在Java 虛擬機器不重新啟動的情況下做出具有載入最新類別檔的功能;不關閉
Java 虛擬機器的情況下,釋放類別所佔用的記憶體,讓記憶體不會因為充滿了
同一個類別的多個版本而面臨記憶體不足的窘境。

 

16、相信在大家的認知裡,都有個基本的概念,就是:「類別是一個樣版,而物
件就是根據這個樣版所產生出來的實體」。在Java 之中,每個類別最後的老祖
宗都是Object,而Object 裡有一個名為getClass()的方法,就是用來取得某
特定實體所屬類別的參考, 這個參考, 指向的是一個名為Class 類別
(Class.class) 的實體,您無法自行產生一個Class 類別的實體,因為它的建構
式被宣告成private,這個Class 類別的實體是在類別檔(.class)第一次載入記憶
體時就建立的,往後您在程式中產生任何該類別的實體,這些實體的內部都會有
一個欄位記錄著這個Class 類別的所在位置基本上,我們可以把每個Class 類別的實體,當作是某個類別在記憶體中的代理人。每次我們需要查詢該類別的資料(如其中的field、method 等)時,就可以
請這個實體幫我們代勞。

 

17、在 Java 之中,每個類別都是由某個類別載入器(ClassLoader 的實體)來載
入,因此,Class 類別的實體中,都會有欄位記錄著載入它的ClassLoader 的
實體(注意:如果該欄位是null,並不代表它不是由類別載入器所載入,而是代表
這個類別由靴帶式載入器(bootstrap loader,也有人稱root loader)所載入,只
不過因為這個載入器並不是用Java 所寫成,所以邏輯上沒有實體 )從上圖我們可以得之,系統裡同時存在多個 ClassLoader 的實體,而且一個類
別載入器不限於只能載入一個類別,類別載入器可以載入多個類別。所以,只要
取得Class 類別實體的參考,就可以利用其getClassLoader()方法籃取得載入
該類別之類別載入器的參考最後,取得了ClassLoader 的實體,我們就可以叫用其loadClass()方法幫我們
載入我們想要的類別,直接使用ClassLoader 類別的loadClass()方法來載入
類別,只會把類別載入記憶體,並不會叫用該類別的靜態初始化區塊,而必須等
到第一次實體化該類別時,該類別的靜態初始化區塊才會被叫用

public class Office
{
public static void main(String args[]) throws Exception
{
Class cb = Office.class ;
System.out.println("類別準備載入") ;
ClassLoader loader = cb.getClassLoader() ;
Class c = loader.loadClass(args[0]) ;
System.out.println("類別準備實體化") ;
Object o = c.newInstance() ;
Object o2 = c.newInstance() ;
}
}

 18、

import java.net.* ;
20
public class Office
{
public static void main(String args[]) throws Exception
{
URL u = new URL("file:/d:/my/lib/") ;
URLClassLoader ucl = new URLClassLoader(new URL[]{ u }) ;
Class c = ucl.loadClass(args[0]) ;
Assembly asm = (Assembly) c.newInstance() ;
asm.start() ;
}
}

 

在這個範例中,我們自己產生 java.net.URLClassLoader 的實體來幫我們
載入我們所需要的類別。但是載入前,我們必須告訴URLClassLoader 去哪個
地方尋找我們所指定的類別才行,所以我們必須給它一個URL 類別所構成的陣
列,代表我們希望它去搜尋的所有位置。URL 可以指向網際網路上的任何位置,
也可以指向我們電腦裡的檔案系統(包含JAR 檔)。在上述範例中,我們希望
URLClassLoader 到d:\my\lib\這個目錄下去尋找我們需要的類別,所以指定
的URL 為”file:/d:/my/lib/”。其實,如果我們請求的位置是主要類別(有public
static void main(String arga[])方法的那個類別)的相對目錄,我們可以在URL
的地方只寫”file:lib/”,代表相對於目前的目錄。您可以發現,同樣一個類別,卻被不同的 URLClassLoader 分別載入,而且分別初始化一次。也就是說,在一個虛擬機器之中,相同的類別被載入了兩次。

 

19、從輸出中我們可以得知,Office.class 由AppClassLoader(又稱做System
Loader,系統載入器)所載入,URL.class 與URLClassLoader.class 由Bootstrap
Loader 所載入(注意:輸出null 並非代表不是由類別載入器所載入。在Java 之
中,所有的類別都必須由類別載入器載入才行,只不過Bootstrap Loader 並非
由Java 所撰寫而成,而是由C++實作而成,因此以Java 的觀點來看,邏輯上
並沒有Bootstrap Loader 的類別實體)。而Word.class 分別由兩個不同的
URLClassLoader 實體載入。至於Assembly.class , 本身應該是由
AppClassLoader 載入,但是由於多型(Polymorphism)的關係,所指向的類別
實體(Word.class)由特定的載入器所載入,導致列印在螢幕上的內容是其所參考
的類別實體之類別載入器。Interface 這種型態本身無法直接使用new 來產生
實體,所以在執行getClassLoader()的時候,叫用的一定是所參考的類別實體
的getClassLoader(),要知道Interface 本身由哪個類別載入器載入,您必須
使用底下程式碼:
Assembly.class.getClassLoader()

 

20、當我們在命令列輸入 java xxx.class 的時候,java.exe 根據我們之前所提
過的邏輯找到了JRE(Java Runtime Environment),接著找到位在JRE 之中的
jvm.dll(真正的Java 虛擬機器),最後載入這個動態聯結函式庫,啟動Java 虛
擬機器。這個動作的詳細介紹請回頭參閱第一章。
虛擬機器一啟動,會先做一些初始化的動作,比方說抓取系統參數等。一旦
初始化動作完成之後,就會產生第一個類別載入器,即所謂的Bootstrap
Loader,Bootstrap Loader 是由C++所撰寫而成(所以前面我們說,以Java
的觀點來看,邏輯上並不存在Bootstrap Loader 的類別實體,所以在Java 程
式碼裡試圖印出其內容的時候,我們會看到的輸出為null),這個Bootstrap
Loader 所做的初始工作中,除了也做一些基本的初始化動作之外,最重要的就
是載入定義在sun.misc 命名空間底下的Launcher.java 之中的
ExtClassLoader( 因為是inner class , 所以編譯之後會變成
Launcher$ExtClassLoader.class),並設定其Parent 為null,代表其父載入器
為Bootstrap Loader。然後Bootstrap Loader 再要求載入定義於sun.misc 命
名空間底下的Launcher.java 之中的AppClassLoader(因為是inner class,
所以編譯之後會變成Launcher$AppClassLoader.class),並設定其Parent
為之前產生的ExtClassLoader 實體。這裡要請大家注意的是,
Launcher$ExtClassLoader.class 與Launcher$AppClassLoader.class 都是
由Bootstrap Loader 所載入,所以Parent 和由哪個類別載入器載入沒有關係。。最後
一個步驟,是由AppClassLoader 負責載入我們在命令列之中所輸入的
xxx.class(注意:實際上xxx.class 很可能由ExtClassLoader 或Bootstrap
Loader 載入,請參考底下「委派模型」一節),然後開始一個Java 應用程式的
生命週期

,類別載入器由誰載入(這句話有點詭異,類別載入器也要由類別載入器載
入,這是因為除了Bootstrap Loader 之外,其餘的類別載入器皆是由Java 撰
寫而成),和它的Parent 是誰沒有關係,Parent 的存在只是為了某些特殊目的,
這個目的我們將在稍後作解釋

在此要請大家注意的是,AppClassLoader 和ExtClassLoader 都是
URLClassLoader 的子類別。由於它們都是URLClassLoader 的子類別,所以它
們也應該有URL 作為搜尋類別檔的參考,由原始碼中我們可以得知,
AppClassLoader 所參考的URL 是從系統參數java.class.path 取出的字串所
決定,而java.class.path 則是由我們在執行java.exe 時,利用 –cp 或
-classpath 或CLASSPATH 環境變數所決定。

 

21、從這個輸出結果,我們可以看出,在預設情況下,AppClassLoader 的搜尋路
徑為”.”(目前所在目錄),如果使用-classpath 選項(與-cp 等效),就可以改變
AppClassLoader 的搜尋路徑,如果沒有指定-classpath 選項,就會搜尋環境
變數CLASSPATH。如果同時有CLASSPATH 的環境設定與-classpath 選項,則
以-classpath 選項的內容為主,CLASSPATH 的環境設定與-classpath 選項兩者
的內容不會有加成的效果。至於 ExtClassLoader 也有相同的情形,不過其搜尋路徑是參考系統參數
java.ext.dirs。輸出結果告訴我們,系統參數java.ext.dirs 的內容,會指向java.exe 所選擇的
JRE 所在位置下的\lib\ext 子目錄

最後一個類別載入器是 Bootstrap Loader,我們可以經由查詢由系統參數
sun.boot.class.path 得知Bootstrap Loader 用來搜尋類別的路徑。

從這三個類別載入器的搜尋路徑所參考的系統參數的名字中,其實還透漏了
一個訊息。請回頭看到java.class.path 與sun.boot.class.path,也就是說,
AppClassLoader 與Bootstrap Loader 會搜尋它們所指定的位置(或JAR 檔),
如果找不到就找不到了,AppClassLoader 與Bootstrap Loader 不會遞迴式地
搜尋這些位置下的其他路徑或其他沒有被指定的JAR 檔。反觀
ExtClassLoader,所參考的系統參數是java.ext.dirs,意思是說,他會搜尋底
下的所有JAR 檔以及classes 目錄,作為其搜尋路徑(所以您會發現上面我們在
測試的時候,如果加入 -Dsun.boot.class.path=c:\winnt 選項時,程式的起
始速度會慢了些,這是因為c:\winnt 目錄下的檔案很多,必須花額外的時間來
列舉JAR 檔)。

在命令列下參數時,使用 –classpath / -cp / 環境變數CLASSPATH 來更
改AppClassLoader 的搜尋路徑, 或者用 –Djava.ext.dirs 來改變
ExtClassLoader 的搜尋目錄, 兩者都是有意義的。可是
用–Dsun.boot.class.path 來改變Bootstrap Loader 的搜尋路徑是無效。這是
因為AppClassLoader 與ExtClassLoader 都是各自參考這兩個系統參數的內
容而建立,當您在命令列下變更這兩個系統參數之後, AppClassLoader 與
ExtClassLoader 在建立實體的時候會參考這兩個系統參數,因而改變了它們搜
尋類別檔的路徑;而系統參數sun.boot.class.path 則是預設與Bootstrap
Loader 的搜尋路徑相同,就算您更改該系統參與,與Bootstrap Loader 完全
無關。

 

更重要的是,AppClassLoader 與ExtClassLoader 在整個虛擬機器之中只
會存有一份,一旦建立了,其內部所參考的搜尋路徑將不再改變,也就是說,即
使我們在程式裡利用System.setProperty()來改變系統參數的內容,仍然無法更
動AppClassLoader 與ExtClassLoader 的搜尋路徑。因此,執行時期動態更
改搜尋路徑的設定是不可能的事情。如果因為特殊需求,有些類別的所在路徑並
非在一開始時就能決定,那麼除了產生新的類別載入器來輔助我們載入所需的類
別之外,沒有其他方法了。

 

22、所謂的委派模型,用簡
單的話來講,就是「類別載入器有載入類別的需求時,會先請示其Parent 使用
其搜尋路徑幫忙載入,如果 Parent 找不到,那麼才由自己依照自己的搜尋路徑
搜尋類別

從輸出我們可以看出,當 AppClassLoader 要載入test.class 時,先請其
Parent,也就是ExtClassLoader 來載入,而ExtClassLoader 又請求其Parent,
即Bootstrap Loader 來載入test.class。由於<JRE 所在目錄>\classes 目錄為

Bootstrap Loader 的搜尋路徑之一, 所以Bootstrap Loader 找到了
test.class,因此將它載入。接著在test.class 之內有載入testlib.class 的需求,
由於test.class 是由Bootstrap Loader 所載入,所以testlib.class 內定是由
Bootstrap Loader 根據其搜尋路徑來尋找,因為testlib.class 也位於Bootstrap
Loader 可以找到的路徑下,所以也被載入了。

從輸出我們可以看出,當 AppClassLoader 要載入test.class 時,先請其
Parent,也就是ExtClassLoader 來載入,而ExtClassLoader 又請求其Parent,
即Bootstrap Loader 來載入test.class。由於<JRE 所在目錄>\classes 目錄為
Bootstrap Loader 的搜尋路徑之一, 所以Bootstrap Loader 找到了
test.class,因此將它載入。接著在test.class 之內有載入testlib.class 的需求,
由於test.class 是由Bootstrap Loader 所載入,所以testlib.class 內定是由
Bootstrap Loader 根據其搜尋路徑來尋找,但是因為Bootstrap Loader 根本
找不到testlib.class(被我們刪除了),而Bootstrap Loader 又沒有Parent,所
以無法載入testlib.clss。

從輸出我們可以看出,當 AppClassLoader 要載入test.class 時,先請其
Parent,也就是ExtClassLoader 來載入,而ExtClassLoader 又請求其Parent,
即Bootstrap Loader 來載入test.class。但是Bootstrap Loader 無法在其搜
尋路徑下找到test.class(被我們刪掉了),所以ExtClassLoader 只得自己搜尋。
因此ExtClassLoader在其搜尋路徑<JRE所在目錄>\lib\ext\classes底下找到
test.class,因此將它載入。接著在test.class 之內有載入testlib.class 的需求,
由於test.class 是由ExtClassLoader 所載入,所以testlib.class 內定是由
ExtClassLoader 根據其搜尋路徑來尋找, 但是因為ExtClassLoader 有
Parent,所以要先由Bootstrap Loader 先幫忙尋找,testlib.class 位於
Bootstrap Loader 可以找到的路徑下,所以被Bootstrap Loader 載入了。

 

「類別載入器可以看到Parent
所載入的所有類別,但是反過來並非除此

 

23、其實不
只是JDBC,在Java 領域中只要分成API(Application Programming
Interface,公開制定,會成為核心類別函式庫(由Bootstrap Loader 載入)或擴
充類別函式庫(由ExtClassLoader 載入))與SPI(Service Provide Interface,由
特定廠商撰寫,會成為擴充類別函式庫(由ExtClassLoader 載入)或應用程式(由
AppClassLoader 載入)的一部分)的函式庫,都會遇到此問題,比方說JDBC 或
JNDI 就是最好的例子。解決這個問題的方法是透過Context Class Loader,不
過本章不對此問題進行討論。

 

24、整個程序是這樣子的: 一開始的時候,我們在命令列輸入java office Word,所
以AppClassLoader 必須負責載入Office.class , 由於其
Parent(ExtClassLoader)與Parent 的Parent(Bootstrap Loader)都無法在其
搜尋路徑找到Office.class , 所以最後是由AppClassLoader 載入
Office.class。我們在Office.class 之中,需要建立URL 與URLClassLoader
的類別實體,預設使用的類別載入器是當時所在的類別本身的類別載入器,也就
是利用ClassLoader.getCallerClassLoader() 取得的類別載入器( 注意,
ClassLoader.getCallerClassLoader()是一個private 的方法,所以我們無法
自行叫用,這是new 運算子本身隱函的呼叫機制中自行使用的)。接下來,
AppClassLoader 仍然會去請求其Parent(ExtClassLoader)與Parent 的
Parent(Bootstrap Loader)來載入,其中,Bootstrap Loader 在<JRE 所在目
錄>\lib\rj.jar 之中找到URL.class 與URLClassLoader.class,所以理所當然由
Bootstrap Loader 負責載入。最後,我們請求URLClassLoader 到相對路徑下
的lib\ 子目錄載入Word.class , 載入Word.class 之前, 必須先載入
Assembly.class,所以URLClassLoader 會請求其Parent(AppClassLoader)、
Parent 的Parent(ExtClassLoader)與Parent 的Parent 的Parent(Bootstrap
Loader)來載入,所以最後會由AppClassLoader 載入Assembly.class,而由
URLClassLoader 載入Word.class。然後因為Assembly.class 的實體(asm 與
asm1)參考到的事Word.class 的實體(c 與c1),所以最後印出結果是
URLClassLoader 載入的,但是如果我們直接用Assembly.getClassLoader(),
就會顯示出是AppClassLoader 載入的。

 

25、從上面的種種測試和說明,我們了解了類別載入器和其載入機制是一個非常
複雜的系統,那麼為何要設計這麼複雜的系統呢? 除了可以達到動態性之外,其
實最重要的原因莫過於安全性。我們以下面這張圖來說明:
ExtClassLoader
Bootstrp Loader
Parent
AppClassLoader
Parent
URLClassLoader
Parent
URLClassLoader
Parent
來自於www.sun.com 來自於www.xxx.com
這張圖說明了兩件事情,第一,假設我們利用 URLClassLoader 到網路上的任
何地方下載了其他的類別,URLClassLoader 都不可能下載AppClassLoader、
ExtClassLoader、或者Bootstrap Loader 可以找到的同名類別(指全名,套件
名稱+類別名稱),因此,蓄意破壞者根本沒有機會植入有問題的程式碼於我們的
電腦之中(除非蓄意破壞者能潛入您的電腦,置換掉您電腦內的類別檔,但是這
已經不是Java 所涉及的安全問題了,而是作業系統本身的安全問題)。第二,
類別載入器無法看到其他相同階層之類別載入器所載入的類別,如上圖所示,圖
中虛線索框起來的部分意指從www.sun.com 下載程式碼的類別載入器所能看
到的類別。告訴我們從www.sun.com 載入的類別,無法看到www.xxx.com
載入的類別,這除了意味著不同的類別載入器可以載入完全相同的類別之外,也
排除了誤用或惡意使用別人程式碼的機會。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值