java的classpath
轉(zhuǎn)自:http://takkymj.javaeye.com/blog/734408
1、class搜索路徑的重要性
理解class搜索路徑對(duì)所有Java開發(fā)人員來說都很重要,但是,IDE的廣泛使用掩蓋了這項(xiàng)技術(shù),使大家普遍對(duì)它缺乏了解,甚至包括好多老鳥。這個(gè)問題在開發(fā)用于發(fā)布的應(yīng)用程序(原文為distributed applications,但好像譯為“分布式應(yīng)用”有點(diǎn)晦澀)時(shí)尤其嚴(yán)重,因?yàn)閼?yīng)用程序運(yùn)行時(shí)的系統(tǒng)環(huán)境可能和開發(fā)時(shí)的大不相同。
本文詳細(xì)描述了某些Java類被其他代碼引用時(shí),Java編譯器和JVM如何使用類搜索路徑(class search path )定位這些類。這兒用一個(gè)非常簡(jiǎn)單的例子——同一個(gè)包中的兩個(gè)類——來具體說明。我們將通過不同的方式來編譯這兩個(gè)類,根據(jù)classpath的設(shè)置不同,編譯可能成功也可能失敗。
為了最清楚的說明這個(gè)問題,我們將只使用命令行工具進(jìn)行編譯。交互式開發(fā)工具有它們自己操作classpath的方法,這些方法因產(chǎn)品而異。
至于是由Java編譯器在編譯時(shí)定位需要的類,還是由JVM在運(yùn)行時(shí)來做,這兩種方法沒有本質(zhì)的區(qū)別。但編譯器可以從源代碼中編譯需要的類,而JVM不行。下面的例子中我們用編譯器來做,但在運(yùn)行時(shí)的實(shí)現(xiàn)也完全類似。
2、例子
本例有兩個(gè)很小的類:com.web_tomorrow.CPTest1 和 com.web_tomorrow.CPTest2,如下所示:
package com.web_tomorrow;
public class CPTest1
{
?? public static void main(String[] args)
?? {
??? System.out.println ("Run CPTest1.main()");
?? }
}
package com.web_tomorrow;
public class CPTest2
{
? public static void main(String[] args)
? {
??? System.out.println ("Run CPTest2.main()");
??? CPTest1 cpt1 = new CPTest1();
? }
}
Java代碼組織的一個(gè)最基本規(guī)則就是`package name = directory name'(“包名 = 目錄名”)。我們將為這兩個(gè)類建立對(duì)應(yīng)的目錄結(jié)構(gòu),它們?cè)诎點(diǎn)om.web_tomorrow中,所以我們創(chuàng)建目錄
com/web_tomorrow來存放源代碼:
[root]
? com
??? web_tomorrow
????? CPTest1.java
????? CPTest2.java
在本文中我用符號(hào)`[root]'來表示存放上述結(jié)構(gòu)的任意目錄,也就是說,根目錄的位置由你決定。當(dāng)然這會(huì)隨安裝這些文件的方式不同而不一樣。
3基本原理
讓我們來嘗試用命令行工具javac來編譯CPTest1.java。為了完全禁止類搜索路徑(以防已有的設(shè)置影響本例),我們?cè)趈avac上加上選項(xiàng)`-classpath ""'。
作為第一次試驗(yàn),我們先換到CPTest1.java所在的目錄下,并且嘗試用javac和文件名進(jìn)行編譯。
cd [root]/com/web_tomorrow
javac -classpath "" CPTest1.java
操作成功,因?yàn)榫幾g器能發(fā)現(xiàn)CPTest1.java (它就在當(dāng)前的工作目錄下),并且CPTest1沒有引用任何其他類。輸出文件CPTest1.class在CPTest1.java的相同目錄下,因?yàn)槟銢]有給編譯器任何信息讓它作其它處理。到現(xiàn)在為止,一直都很好。現(xiàn)在讓我們用同樣的方式來試一下CPTest2。仍然在web_tomorrow目錄下,執(zhí)行命令:
javac -classpath "" CPTest2.java
雖然目錄還是剛才的目錄,CPTest1和CPTest2也在相同的包里,這一次卻失敗了。錯(cuò)誤信息可能是這樣的:
CPTest2.java:7: cannot resolve symbol
symbol? : class CPTest1
location: class com.web_tomorrow.CPTest2
? CPTest1 cpt1 = new CPTest1();
? ^
這一次和上一次成功的情況之間的區(qū)別之一就是CPTest2中有對(duì)CPTest1的引用:
? CPTest1 cpt1 = new CPTest1();
這次發(fā)生了什么呢?當(dāng)編譯器遇到對(duì)CP1Test的引用時(shí),它會(huì)認(rèn)為CP1Test類與當(dāng)前編譯的CP2Test類在同一個(gè)包里。這個(gè)假定是正確的,于是編譯器來尋
找com.web_tomorrow.CP1Test,但它沒有地方可以找,因?yàn)槲覀円呀?jīng)把類搜索路徑明確的指定成了“”(也就是空)。
你可能認(rèn)為,只要告訴編譯器在當(dāng)前目錄下尋找就可以解決這個(gè)問題。在Unix和Windows系統(tǒng)中,“當(dāng)前目錄”的標(biāo)準(zhǔn)符號(hào)都是一個(gè)點(diǎn)號(hào)(.),也就是這樣的命
令:
javac -classpath "." CPTest2.java
Fail Againt!跟剛才的例子完全一樣,現(xiàn)在的問題是雖然CPTest1.java在當(dāng)前目錄下,但它實(shí)現(xiàn)的類不是CPTest1,而是 com.web_tomorrow.CPTest1,編譯器將在當(dāng)前目錄下尋找目錄com/web_tomorrow。也就是說,它在目錄[root] /com/web_tomorrow/com/web_tomorrow中尋找一個(gè)Java源文件或類文件,它們事實(shí)上根本不存在,于是出錯(cuò)。
為了讓編譯器工作正常,我們不能讓類搜索路徑指向包含CPTest1的目錄,而是按照J(rèn)ava標(biāo)準(zhǔn)中`package name = directory name'的規(guī)則,給類搜索路徑指定編譯器能找到CPTest1的根目錄。這樣才能工作,雖然不太好看:
javac -classpath "../.." CPTest2.java
在考慮如何變得不難看之前,看一下這個(gè)例子(仍在同一目錄下)
javac -classpath "" CPTest1.java CPTest2.java
盡管classpath為空,這次卻能工作。這是因?yàn)镴ava編譯器會(huì)在命令行中明確列出的所有源代碼中查找引用。如果要編譯同一目錄下的多個(gè)類,有一種很簡(jiǎn)單的方式:
javac -classpath "" *.java
'*.java'擴(kuò)展為當(dāng)前目錄下所有.java文件的列表。這就說明為什么一次編譯多個(gè)文件會(huì)成功,而同樣的文件一個(gè)一個(gè)的編譯卻失敗。
編譯CPTest2有一個(gè)更方便的方法:
cd [root]
javac -classpath "." com/web_tomorrow/CPTest2.java
這次我們?yōu)镃Ptest2.java指定了完整的路徑,而在-classpath選項(xiàng)中使用了'.'。另外,我們沒有告訴編譯器在當(dāng)前目錄下尋找文件,而是讓它從當(dāng)前目錄開始搜索。因?yàn)槲覀円业念愂莄om.web_tomorrow.CPTest1,編譯器將在./com /web_tomorrow(也就是說當(dāng)前目錄下的com/web_tomorrow子目錄)下尋找。這才是CPTest1.java正確的位置。
事實(shí)上,盡管我在命令行只指定了CPTest2,但事實(shí)上CPTest1同時(shí)也會(huì)被編譯。編譯器在正確的位置找到這個(gè).java文件,但它不能判定這個(gè).java文件是否包含正確的類,所以它編譯這個(gè)文件。但請(qǐng)注意如果是:
cd [root]
javac -classpath "." com/web_tomorrow/CPTest1.java
就不會(huì)導(dǎo)致CPTest2.java被編譯,因?yàn)榫幾g器編譯CPTest1時(shí)不需要知道CPTest2的任何事情。
類文件與.java文件分開的情況
到目前為止所有成功的例子都把輸出的.class文件放到.java文件同樣的位置,這是一種比較簡(jiǎn)單的模式,應(yīng)用非常廣泛。但很多開發(fā)者喜歡把源文件和生成的文件分開,因此它必須告訴編譯器為.class文件維護(hù)不同的目錄。下面我們來看看這對(duì)類搜索路徑有何影響。
首先我們刪除剛才幾個(gè)例子產(chǎn)生的所有.class文件。我們還要有一個(gè)新的目錄來存放生成的.class文件。命令行方式下的操作過程如下:
cd [root]
rm com/web_tomorrow/*.class
mkdir classes
如果你用Windows系統(tǒng),別忘了把'/'替換成'/',現(xiàn)在的目錄結(jié)構(gòu)如下:
[root]
? com
??? web_tomorrow
????? CPTest1.java
????? CPTest2.java
? classes
現(xiàn)在編譯CPTest1.java,指定類文件的目標(biāo)目錄(通過-d選項(xiàng))
cd [root]
javac -d classes -classpath "" com/web_tomorrow/CPTest1.java
成功,但你會(huì)注意到,.class文件根本不是直接放在classes目錄中,而是有了一個(gè)新的目錄結(jié)構(gòu):
[root]
? com
??? web_tomorrow
????? CPTest1.java
????? CPTest2.java
? classes
??? com
????? web_tomorrow
??????? CPTest1.class
編譯器建立了一個(gè)符合包結(jié)構(gòu)的目錄結(jié)構(gòu),這很有用,我們馬上就會(huì)看到。當(dāng)開始編譯CPTest2.java時(shí)我們有兩個(gè)選擇,第一種是像上面一樣,讓編譯器同時(shí)也
編譯一次CPTest1;另一種是用-classpath選項(xiàng)指定剛才編譯好的.class文件,這種方法更好一點(diǎn),因?yàn)槲覀儾槐卦賮砭幾g一遍CPTest1:
cd [root]
javac -d classes -classpath classes com/web_tomorrow/CPTest2.java
完成之后,我們的目錄結(jié)構(gòu)如下:
[root]
? com
??? web_tomorrow
????? CPTest1.java
????? CPTest2.java
? classes
??? com
????? web_tomorrow
??????? CPTest1.class
??????? CPTest2.class
當(dāng)然我們也可以在同一條命令中編譯兩個(gè).java文件,結(jié)果完全相同。
4、classpath中的JAR打包文件
Java編譯器和運(yùn)行環(huán)境不僅可以在獨(dú)立文件中搜索類,還可以在JAR打包文件中進(jìn)行查找。一個(gè)JAR打包文件可以維護(hù)它自己的目錄結(jié)構(gòu),Java 按照跟普通目錄結(jié)構(gòu)完全相同的方式,`directory name = package name',進(jìn)行搜索。由于JAR本身就是一個(gè)目錄,所以在類搜索路徑中包含JAR打包文件時(shí),路徑必須引用JAR本身,而不是它所在的目錄。如果我在目錄/myclasses下有一個(gè)JAR myclasses.jar,我需要指定:
javac -classpath /myclasses/myclasses.jar ...
而不僅僅是目錄myclasses。
5、多個(gè)類搜索目錄
在上面的例子中,每次我們都只讓javac在一個(gè)目錄下搜索。實(shí)際工作中,你的類搜索路徑會(huì)包含很多目錄和JAR文件。Javac的-classpath選項(xiàng)允許指定多個(gè)位置,但請(qǐng)注意,它的具體語法在Unix系統(tǒng)和Windows系統(tǒng)中稍有區(qū)別。
在Unix系統(tǒng)中是:
javac -classpath dir1:dir2:dir3 ...
而Windows系統(tǒng)則是:
javac -classpath dir1;dir2;dir3 ...
這是因?yàn)閃indows使用冒號(hào)(:)作為文件名的一部分,因此不能作為文件名分隔符。當(dāng)然目錄分隔符也不一樣:Unix的正斜杠(/)和Windows的反斜杠(/)。
6、系統(tǒng)classpath
除了在javac命令指定類搜索路徑外,我們還可以使用'系統(tǒng)'類路徑。如果命令中沒有指定特定的路徑,Java編譯器和JVM都使用系統(tǒng)路徑。在Unix和Windows系統(tǒng)中,系統(tǒng)路徑都通過環(huán)境變量設(shè)置。例如在使用bash shell的Linux系統(tǒng)中:
CLASSPATH=/myclasses/myclasses.jar;export CLASSPATH
在Windows中:
set CLASSPATH=c:/myclasses/myclasses.jar
這是暫時(shí)改變系統(tǒng)變量CLASSPATH的好方法,但如果你想這些變化一直保留下來,你就需要針對(duì)你的系統(tǒng)做一些修改。例如在Linux系統(tǒng)中,我會(huì)將這個(gè)命令放到我目錄下的文件.bashrc中。而在Windows 2000/NT中可以通過‘控制面板’來修改。
如果你有很多隨時(shí)用到的JAR,設(shè)置系統(tǒng)變量CLASSPATH就顯得尤為重要。比如說,如果我在用Sun的J2EE參考實(shí)現(xiàn)開發(fā)EJB應(yīng)用,所有 EJB相關(guān)的類都發(fā)布在一個(gè)叫做“j2ee.jar”的JAR打包文件中,我希望這個(gè)JAR一直都在類搜索路徑中。另外,還有很多人希望無論當(dāng)前目錄是什么,搜索路徑始終包含當(dāng)前目錄。所以在我的.bashrc文件中就有這樣一行:
CLASSPATH=/usr/j2ee/j2ee.jar:.;export CLASSPATH
其中`.'是指'當(dāng)前目錄'
很容易看到命令行中的-classpath選項(xiàng)覆蓋默認(rèn)的系統(tǒng)類路徑;它不是擴(kuò)展而是覆蓋。因此如果我們既要包含默認(rèn)的系統(tǒng)路徑,又要增加一些時(shí)怎么處理呢?我們可以簡(jiǎn)單的通過-classpath選項(xiàng)同時(shí)列出默認(rèn)值和我們要額外增加的部分。而一個(gè)更好的辦法是引用系統(tǒng)變量CLASSPATH。當(dāng)然,這個(gè)語法對(duì)Windows系統(tǒng)和Unix系統(tǒng)是不同的。在Unix上:
javac -classpath $CLASSPATH:dir1:dir2 ...
其中$CLASSPATH擴(kuò)展為系統(tǒng)變量CLASSPATH。在Windows上:
javac -classpath %CLASSPATH%;dir1:dir2 ...
轉(zhuǎn)載于:https://www.cnblogs.com/quechao123/archive/2010/10/28/2346630.html
總結(jié)
以上是生活随笔為你收集整理的java的classpath的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到掉了一嘴牙怎么回事
- 下一篇: 做梦梦到被鬼压床怎么回事