溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何深入探討、理解Java的CLASSPATH

發(fā)布時間:2021-12-27 17:05:42 來源:億速云 閱讀:124 作者:柒染 欄目:編程語言

這篇文章給大家介紹如何深入探討、理解Java的CLASSPATH,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

從表面上看,Java的classpath(類路徑)很簡單,但一直以來它都是一個產(chǎn)生問題和混亂的根源。本文介紹classpath的基本知識、可能產(chǎn)生的問題,并提供了一個簡單的classpath管理工具。

和Java類路徑(classpath)打交道的過程中,開發(fā)者偶爾會遇到麻煩。這是因為,類裝載器實際裝入的是哪一個類有時并不顯而易見,當(dāng)應(yīng)用程序的classpath包含大量的類和目錄時,情況尤其嚴(yán)重。本文將提供一個工具,它能夠顯示出被裝入類文件的絕對路徑名。

一、Classpath基礎(chǔ)

Java虛擬機(JVM)借助類裝載器裝入應(yīng)用程序使用的類,具體裝入哪些類根據(jù)當(dāng)時的需要決定。CLASSPATH環(huán)境變量告訴類裝載器到哪里去尋找第三方提供的類和用戶定義的類。另外,你也可以使用JVM命令行參數(shù)-classpath分別為應(yīng)用程序指定類路徑,在-classpath中指定的類路徑覆蓋CLASSPATH環(huán)境變量中指定的值。

類路徑中的內(nèi)容可以是:文件的目錄(包含不在包里面的類),包的根目錄(包含已打包的類),包含類的檔案文件(比如.zip文件或者.jar文件)。在Unix家族的系統(tǒng)上,類路徑的各個項目由冒號分隔,在MS Windows系統(tǒng)上,它們由分號分隔。

類裝載器以委托層次的形式組織,每一個類裝載器有一個父類裝載器。當(dāng)一個類裝載器被要求裝載某個類時,它在嘗試自己尋找類之前會把請求先委托給它的父類裝載器。系統(tǒng)類裝載器,即由安裝在系統(tǒng)上的JDK或JRE提供的默認(rèn)類裝載器,通過CLASSPATH環(huán)境變量或者-classpath這個JVM命令行參數(shù)裝入第三方提供的類或者用戶定義的類。系統(tǒng)類裝載器委托擴展類裝載器裝入使用Java Extension機制的類。擴展類裝載器委托自舉類裝載器(bootstrap class loader)裝入核心JDK類。

你可以自己開發(fā)特殊的類裝載器,定制JVM如何動態(tài)地裝入類。例如,大多數(shù)Servlet引擎使用定制的類裝載器,動態(tài)地裝入那些在classpath指定的目錄內(nèi)發(fā)生變化的類。

必須特別注意的是(也是令人吃驚的是),類裝載器裝入類的次序就是類在classpath中出現(xiàn)的次序。類裝載器從classpath的第一項開始,依次檢查每一個設(shè)定的目錄和壓縮文件,嘗試找出待裝入的類文件。當(dāng)類裝載器第一次找到具有指定名字的類時,它就把該類裝入,classpath中所有余下的項目都被忽略。

看起來很簡單,對吧?

二、可能出現(xiàn)的問題

不管他們是否愿意承認(rèn),初學(xué)者和富有經(jīng)驗的Java開發(fā)者都一樣,他們都曾經(jīng)在某些時候(通常是在那些最糟糕的情形下)被冗長、復(fù)雜的classpath欺騙。應(yīng)用程序所依賴的第三方類和用戶定義類的數(shù)量逐漸增長,classpath也逐漸成了一個堆積所有可能的目錄和檔案文件名的地方。此時,類裝載器首先裝載的究竟是哪一個類也就不再顯而易見。如果classpath中包含重復(fù)的類入口,這個問題尤其突出。前面已經(jīng)提到,類裝載器總是裝載第一個它在classpath中找到的具有合適名字的類,從實際效果看,它“隱藏”了其他具有合適名字但在classpath中優(yōu)先級較低的類。

如果不小心,你很容易掉進(jìn)這個classpath的陷阱。當(dāng)你結(jié)束了一天漫長的工作,最后為了讓應(yīng)用程序使用最好、最新的類,你把一個目錄加入到了classpath,但與此同時,你卻忘記了:在classpath的另一個具有更高優(yōu)先級的目錄下,存放著該類的另一個版本!

三、一個簡單的classpath工具

優(yōu)先級問題是扁平路徑聲明方法與生俱來固有的問題,但它不是只有Java的classpath才有的問題。要解決這個問題,你只需站到富有傳奇色彩的軟件巨構(gòu)的肩膀上:Unix操作系統(tǒng)有一個which命令,在命令參數(shù)中指定一個名字,which就會顯示出當(dāng)這個名字作為命令執(zhí)行時執(zhí)行文件的路徑名。實際上,which命令是分析PATH變量,然后找出命令第一次出現(xiàn)的位置。對于Java的類路徑管理來說,這應(yīng)該也是一個好工具。在它的啟發(fā)之下,我著手設(shè)計了一個Java工具JWhich。這個工具要求指定一個Java類的名字,然后根據(jù)classpath的指引,找出類裝載器即將裝載的類所在位置的絕對路徑。

下面是一個JWhich的使用實例。它顯示出當(dāng)Java類裝載器裝載com.clarkware.ejb.ShoppingCartBean類時,該類第一次出現(xiàn)位置的絕對路徑名,查找結(jié)果顯示該類在某個目錄下:

> java JWhich com.clarkware.ejb.ShoppingCartBean

Class 'com.clarkware.ejb.ShoppingCartBean' found in '/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class'

下面是第二個JWhich的使用實例。它顯示出當(dāng)Java類裝載器裝載javax.servlet.http.HttpServlet類時,該類第一次出現(xiàn)位置的絕對路徑名,查找結(jié)果顯示該類在某個檔案文件中:

> java JWhich javax.servlet.http.HttpServlet

Class 'javax.servlet.http.HttpServlet' found in 'file:/home/mclark/lib/servlet.jar!/javax/servlet/http/HttpServlet.class'

四、JWhich的工作過程

要精確地測定classpath中哪一個類先被裝載,你必須深入到類裝載器的思考方法。事實上,具體實現(xiàn)的時候并沒有聽起來這么復(fù)雜——你只需直接詢問類裝載器就可以了!

1: public class JWhich {

2:

3: /**

4: * 根據(jù)當(dāng)前的classpath設(shè)置,

5: * 顯示出包含指定類的類文件所在

6: * 位置的絕對路徑

7: *

8: * @param className <類的名字>

9: */

10: public static void which(String className) {

11:

12: if (!className.startsWith("/")) {

13: className = "/" + className;

14: }

15: className = className.replace('.', '/');

16: className = className + ".class";

17:

18: java.net.URL classUrl =

19: new JWhich().getClass().getResource(className);

20:

21: if (classUrl != null) {

22: System.out.println("nClass '" + className +

23: "' found in n'" + classUrl.getFile() + "'");

24: } else {

25: System.out.println("nClass '" + className +

26: "' not found in n'" +

27: System.getProperty("java.class.path") + "'");

28: }

29: }

30:

31: public static void main(String args[]) {

32: if (args.length > 0) {

33: JWhich.which(args[0]);

34: } else {

35: System.err.println("Usage: java JWhich");

36: }

37: }

38: }

首先,你必須稍微調(diào)整一下類的名字以便類裝載器能夠接受(12-16行)。在類的名字前面加上一個“/”表示要求類裝載器對classpath中的類名字進(jìn)行逐字精確匹配,而不是嘗試隱含地加上調(diào)用類的包名字前綴。把所有“.”轉(zhuǎn)換為“/”的目的是,按照類裝載器的要求,把類名字格式化成一個合法的URL資源名。

接下來,程序向類裝載器查詢資源,這個資源的名字必須和經(jīng)過適當(dāng)格式化的類名字匹配(18-19行)。每一個Class對象維護(hù)著一個對裝載它的ClassLoader對象的引用,所以這里是向裝載JWhich類的類裝載器查詢。Class.getResource()方法實際上委托裝入該類的類裝載器,返回一個用于讀取類文件資源的URL;或者,當(dāng)指定的類名字不能在當(dāng)前的classpath中找到時,Class.getResource()方法返回null。

最后,如果當(dāng)前的classpath中能夠找到指定的類,則程序顯示包含該類的類文件所在位置的絕對路徑名(21-24行)。作為一種調(diào)試輔助手段,如果當(dāng)前classpath中不能找到指定的類,則程序獲取java.class.path系統(tǒng)屬性并顯示當(dāng)前的classpath(24-28行)。

很容易想象,在使用Servlet引擎classpath的Java Servlet中,或者在使用EJB服務(wù)器classpath的EJB組件中,上面這段簡單的代碼是如何運作。例如,如果JWhich類是由Servlet引擎的定制類裝載器裝入,那么程序?qū)⒂肧ervlet引擎的類裝載器去尋找指定的類。如果Servlet引擎的類裝載器不能找到類文件,它將委托它的父類裝載器。一般地,當(dāng)JWhich被某個類裝載器裝入時,它能夠找出當(dāng)前類裝載器以及所有其父類裝載器所裝入的所有類。

如果需要是所有發(fā)明之母,那么幫助我們管理Java類路徑的工具可以說遲到了很長時間。Java新聞組和郵件列表中充塞著許多有關(guān)classpath的問題,現(xiàn)在JWhich為我們提供了一個簡單卻強大的工具,幫助我們在任何環(huán)境中徹底玩轉(zhuǎn)Java類路徑。

關(guān)于如何深入探討、理解Java的CLASSPATH就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI