<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當你滿懷著希望安裝好了 java, 然後興沖沖地寫了個 hello world,然後編譯,執行, 就等著那兩個美好的單詞出現在眼前, 可是不幸的是, 只看到了 Can't find class HelloWorld 或者 Exception in thread "main" java.lang.NoSuchMethodError : maain.為什麼呢? 編譯好的 class 明明在呀.
我們一起來看一看 java 程式的執行過程. 我們已經知道 java 是通過 java虛擬機器器來解釋執行的, 也就是通過 java 命令, javac 編譯生成的 .class檔案就是虛擬機器器要執行的程式碼, 稱之為位元組碼(bytecode), 虛擬機器器通過 classloader來裝載這些位元組碼, 也就是通常意義上的類. 這裡就有一個問題, classloader 從哪裡知道 java 本身的類庫及使用者自己的類在什麼地方呢? 或者有著預設值(當前路徑).或者要有一個使用者指定的變數來表明, 這個變數就是類路徑(classpath), 或者在執行的時候傳引數給虛擬機器器. 這也就是指明 classpath 的三個方法. 編譯的過程和執行的過程大同小異, 只是一個是找出來編譯, 另一個是找出來裝載. 實際上 java 虛擬機器器是由 java luncher 初始化的, 也就是 java (或 java.exe)這個程式來做的. 虛擬機器器按以下順序搜尋並裝載所有需要的類:
那麼到底該怎麼做呢? 使用者類路徑就是一些包含類檔案的目錄, .jar, .zip 檔案的列表, 至於類具體怎麼找, 因為牽扯到 package 的問題, 下面將會說到, 暫時可認為只要包含了這個類就算找到了這個類. 根據平臺的不同分隔符略有不同, 類 unix 的系統基本上都是 ":", windows 多是 ";". 其可能的來源是:
我們舉個 HelloWorld 的例子來說明. 先做以下假設:
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!n"); System.exit(0); } }
首先這個檔案一定要寫對, 如果對 c 熟悉的話, 很有可能寫成這樣:
public static void main(int argc, String[] argv) { .... }
這樣是不對的, 不信可以試一試. 由於手頭沒有 java 的規範, 所以作如下猜想: java 的 application 程式, 必須以 public static void main(String[])開始, 其他不一樣的都不行.
到現在為止, 我們設定方面只設定了 PATH.
1, 當前路徑就是指你的 .class 檔案在當前目錄下,
[HelloWorld]$ javac HelloWorld.java //這一步不會有多大問題, [HelloWorld]$ java HelloWorld // 這一步可能就會有問題.
如果出了象開頭那樣的問題, 首先確定不是由於敲錯命令而出錯. 如果沒有敲錯命令,那麼接著做:
[HelloWorld]$ echo $CLASSPATH 或者 c:HelloWorld>echo %CLASSPATH%
看看 CLASSPATH 環境變數是否設定了, 如果設定了, 那麼用以下命令:
[HelloWorld]$ CLASSPATH= 或者 c:HelloWorld> set CLASSPATH=
來使它為空, 然後重新執行. 這次使用者類路徑預設的是 ".", 所以應該不會有相同的問題了. 還有一個方法就是把 "." 加入到 CLASSPATH 中.
[/]$ CLASSPATH=$CLASSPATH:. 或者 c:HelloWorld> set CLASSPATH=%CLASSPATH%;.
同樣也可以成功. Good Luck.
2, 當你的程式需要第三方的類庫支援, 而且比較常用, 就可以採用此種方法.比如常用的資料庫驅動程式, 寫 servlet 需要的 servlet 包等等. 設定方法就是在環境變數中加入 CLASSPATH. 然後就可以直接編譯執行了. 還是以 HelloWorld 為例, 比如你想在根目錄中執行它, 那麼你直接在根目錄下執行
$ java HelloWorld 或者 c:>java HelloWorld
這樣肯定會出錯, 如果你的 CLASSPATH 沒有改動的話. 我想大家應該知道為什麼錯了吧, 那麼怎麼改呢? 前面說過, 使用者類路徑就是一些包含你所需要的類的目錄, .jar 檔案包, .zip 包. 現在沒有生成包, 所以只好把 HelloWorld.class 所在的目錄加到 CLASSPAT了, 根據前面的做法, 再執行一次, 看看, 呵呵, 成功了, 換個路徑, 又成功了!! 不僅僅可以直接執行其中的類, 當你要 import 其中的某些類時, 同樣處理. 不知道你想到沒有, 隨著你的系統的不斷的擴充, (當然了, 都是一些需要 java 的東西).如果都加到這個環境變數裡, 那這個變數會越來越臃腫, 雖然環境變數空間可以開很大, 總覺得有些不舒服. 看看下面一個方法.
3, 在命令列引數中指明 classpath. 還是和上面相同的目標, 在任何目錄下執行 HelloWorld, 用這個方法怎麼實現呢?
[/]$ java -cp /HelloWorld HelloWorld 或者 c:>java -cp c:HelloWorld HelloWorld
就可以了. 這是這種方法的最簡單的應用了. 當你使用了另外的包的時候, 還可以採用用這種方法.
例如:
$ javac -classpath aPath/aPackage.jar:. myJava.java $ java -cp aPath/aPackage.jar:. myJava 或者 c:> javac -classpath aPathaPackage.jar;. myJava.java c:> java -cp aPathaPackage.jar;. myJava
這種方法也有一個不方便的的地方就是當第三方包所在的路徑較長或者需要兩個以上包的時候, 每次編譯執行都要寫很長, 非常不方便, 這時候可以寫指令碼來解決.
比如一個例子:
compile (檔案, 許可權改為可執行, 當前目錄) $ cat compile --------------------------- #!/bin/bash javac -classpath aPathaPackage.jar:anotherPathanotherPackage.jar:. m yJavva.java --------------------------- run (檔案, 許可權改為可執行, 當前目錄) $cat run --------------------------- #!/bin/bash java -cp aPathaPackage.jar:anotherPathanotherPackage.jar:. myJava --------------------------- 或者: compile.bat c:HelloWorld> type compile.bat ------------------------- javac -classpath aPathaPackage.jar:anotherPathanotherPackage.jar:. m yJavva.java ------------------------- run.bat c:HelloWorld> type run.bat ------------------------ java -cp aPathaPackage.jar:anotherPathanotherPackage.jar:. myJava ------------------------
就可以了. 試試看.
前面提到了擴充套件類, 擴充套件類是什麼呢? java 的擴充套件類就是應用程式開發者用來擴充套件核心平臺功能的 java 類的包(或者是 native code). 虛擬機器器能像使用系統類一樣使用這些擴充套件類. 有人建議可以把包放入擴充套件目錄裡, 這樣, CLASSPATH 也不用設了,也不用指定了, 豈不是很方便? 確實可以正確執行, 但是個人認為這樣不好, 不能什麼東西都往裡擱, 一些標準的擴充套件包可以, 比如, JavaServlet, Java3D 等等. 可以提個建議, 加一個環境變數, 比如叫 JARPATH, 指定一個目錄, 專門存放使用者的 jar zip等包, 這個要等 SUN 公司來做了. windows98 下, 我原來安裝的時候, 一直裝不上, 總是宕機, 好不容易裝上了, 預設的是不能執行正確的, 然後把 tool.jar 放入 CLASSPATH 後工作正常. 現在作測試,去掉仍然是正確的. 經過多次測試, 發現如果原來曾裝過 jdk 的都很好, 沒有裝過的,裝的時候會宕機, 多裝幾次就可以了. 如果你發現正確安裝後, 不能正常工作, 就把tools.jar 加入 CLASSPATH, 試一下.
Java 中的 "包" 是一個比較重要的概念, package 是這樣定義的:Definition: A package is a collection of related classes and interfaces that provides access protection and namespace management. 也就是: 一個包就是一些提供存取保護和名稱空間管理的相關類與介面的集合. 使用包的目的就是使類容易查詢使用, 防止命名衝突, 以及控制存取. 這裡我們不討論關於包的過多的東西, 只討論和編譯, 執行, 類路徑相關的東西.至於包的其他內容, 請自己查閱相關檔案. 簡單一點來說, 包就是一個目錄, 下面的子包就是子目錄, 這個包裡的類就是這個目錄下的檔案. 我們用一個例子來說明.
首先建目錄結構如下: PackageTest/source/, 以後根目錄指的是 PackageTest目錄, 我們的源程式放在 source 目錄下.
源程式如下:
PackageTest.java package pktest; import pktest.subpk.*; public class PackageTest { private String value; public PackageTest(String s) { value = s; } public void printValue() { System.out.println("Value of PackageTest is " + value); } public static void main(String[] args) { PackageTest test = new PackageTest("This is a Test Package"); test.printValue(); PackageSecond second = new PackageSecond("I am in PackageTest"); second.printValue(); PackageSub sub = new PackageSub("I am in PackageTest"); sub.printValue(); System.exit(0); } } PackageSecond.java package pktest; public class PackageSecond { private String value; public PackageSecond(String s) { value = s; } public void printValue() { System.out.println("Value of PackageSecond is " + value); } } PackageSub.java package pktest.subpk; import pktest.*; public class PackageSub { private String value; public PackageSub(String s) { value = s; } public void printValue() { PackageSecond second = new PackageSecond("I am in subpackage."); second.printValue(); System.out.println("Value of PackageSub is " + value); } } Main.java import pktest.*; import pktest.subpk.*; public class Main() { public static void main() { PackageSecond second = new PackageSecond("I am in Main"); second.printValue(); PackageSub sub = new PackageSub("I am in Main"); sub.printValue(); System.exit(0); } }
其中,Main.java是包之外的一個程式,用來測試包外的程式存取包內的類,PackageTest.java屬於pktest這個包,也是主程式.PackageSecond.java也屬於pktest,PackageSub屬於pktest下的subpk包,也就是pktest.subpk.詳細使用情況,請參看源程式。
好了, 先把源程式都放在 source 目錄下, 使 source 成為當前目錄, 然後編譯一下, 呵呵, 出錯了,Main.java:1: Package pktest not found in import.import pktest.*; 這裡涉及到類路徑中包是怎麼查詢的, 前面我們做了一點假設: "只要包含了這個類就算找到了這個類", 現在就有問題了. 其實 jdk 的 工具 javac javajavadoc 都需要查詢類, 看見目錄, 就認為是包的名字, 對於import 語句來說,一個包對應一個目錄. 這個例子中, import pktest.*, 我們知道類路徑可以包含一個目錄, 那麼就以那個目錄為根, 比如有個目錄 /myclass, 那麼就會在查詢/myclass/pktest 目錄及其下的類. 所有的都找遍, 如果沒有就會報錯. 由於現在的類路徑只有當前目錄, 而當前目錄下沒有 pktest 目錄, 所以就會出錯. 類路徑還可以包含 .jar .zip 檔案, 這些就是可以帶目錄的壓縮包, 可以把 .jar .zip檔案看做一個虛擬的目錄, 然後就和目錄一樣對待了.
好了, 應該知道怎麼做了吧, 修改後的目錄結構如下:
PackageTest | |__source Main.java | |__pktest PackageTest.java PackageSecond.java | |__subpk PackageSub.java
然後重新編譯, 執行, 哈哈, 通過了. 我們再來執行一下 PackageTest.
[source]$ java pktest/PackageTest
怎麼又出錯了?
Exception in thread "main" java.lang.NoClassDefFoundError: pktest/PackageTest 是這樣的, java 所要執行的是一個類的名字, 它可不管你的類在什麼地方, 就象我們前面所討論的一樣來查詢這個類, 所以它把 pktest/PackageTest 看成是一個類的名字了, 當然會出錯了, 應該這麼做,
[source]$ java pktest.PackageTest
大家應該明白道理吧, 我就不多說了. 注意 javac 不一樣, 是可以指明原始檔路徑的, javac 只編譯, 不執行, 查詢類也只有在原始檔中碰到 import 時才會做, 與原始檔所在的包沒有關係. 似乎還又些不好的地方, 怎麼生成的 .class 檔案這麼分散呀, 看著真彆扭. 別急,javac 有一個 -d 命令列引數, 可以指定一個目錄, 把生成的 .class 檔案按照包給你好好地擱在這個目錄裡面.
[source]$ mkdir classes [source]$ javac -d classes pktest/PackageTest.java [source]$ javac -d classes Main.java
那麼執行怎麼執行呢?
[source]$ cd classes [classes]$ java pktest.PackageTest [classes]$ java Main
就可以了. 其實 jdk 的這一套工具小巧簡單, 功能強大, 不會用或者用錯其實不關工具的事, 關鍵是明白工具背後的一些原理和必要的知識. 整合環境是很好,但是它遮蔽了很多底層的知識, 不出錯還好, 一旦出錯, 如果沒有這些必要的知識就很難辦, 只好上 bbs 問, 別人只告訴了你解決的具體方法, 下一次遇到稍微變化一點的問題又不懂了. 所以不要拘泥於工具, java 的這一套工具組合起來使用, 中小型工程(五六十個類), 還是應付得下來的.
以下把 .jar .zip 都看做是 .jar 檔案.
1, 從前面我們可以看出來 jar 檔案在 java 中非常重要, 極大地方便了使用者的使用. 我們也可以做自己的 .jar 包.
還是使用前面那個例子, Main.java 是包之外的東西, 用了 pktest 包中的類,我們現在就是要把 pktest 做成一個 .jar 包, 很簡單, 剛才我們已經把 pktest中的 .class 都集中起來了,
就會生成 mypackage.jar 檔案, 測試一下, 剛才我們生成的 Main.class 就在classes 目錄下, 所以, 從前面可以知道:
[classes]$ java -cp mypackage.jar:. Main
就可以執行了.
2, 如果你看過 jdk 所帶的例子, 你就會知道, .jar 還可以直接執行,
[/demo]$ java -jar aJar.jar
那好, 就那我們的試一試,
看來我們的 jar 和它的 jar 還不一樣, 有什麼不一樣呢? 拿它一個例子出來,重新編譯, 生成 .jar 檔案, 比較後發現, 是 .jar 壓縮包中 META-INF/MANIFEST.MF檔案不一樣, 多了一行, Main-Class: xxxxx, 再看看出錯資訊, 原來是沒有指定Main-Class, 看看 jar 命令, 發現有一個引數 -m,-m include manifest information from specified manifest file
和出錯資訊有點關係, 看來它要讀一個配製檔案. 只好照貓畫虎寫一個了.
[classes]$ cat myManifest Manifest-Version: 1.0 Main-Class: pktest.PackageTest Created-By: 1.2.2 (Sun Microsystems Inc.) [classes]$ jar -cvfm mypackage.jar myManifest pktest added manifest adding: pktest/(in = 0) (out= 0)(stored 0%) adding: pktest/PackageSecond.class(in = 659) (out= 395)(deflated 40%) adding: pktest/subpk/(in = 0) (out= 0)(stored 0%) adding: pktest/subpk/PackageSub.class(in = 744) (out= 454)(deflated 38%) adding: pktest/PackageTest.class(in = 1041) (out= 602)(deflated 42%) [classes]$ java -jar mypackage.jar Value of PackageTest is This is a Test Package Value of PackageSecond is I am in PackageTest Value of PackageSecond is I am in subpackage. Value of PackageSub is I am in PackageTest
好了, 成功了, 這樣就做好了一個可以直接執行的 .jar 檔案. 大家可以自己試一試
做一個以 Main 為主程式的可執行的 jar.
小結:
這篇文章中, 我們討論了 java 中的 class path, package, jar 等基本但比較重要的東西, 主要是 class path. 並不是簡單的一份 CLASSPATH 的完全功略, 而是試圖讓讀者明白其原理, 自己思考, 自己動手. 其實大多數東西都在 sun 的 java doc中都有, 我只不過結合例子稍微談了一下, 希望能有所幫助. 由於條件所限, 只測試了jdk1.2.2 在 98 及 linux 的情況, 其他版本的 jdk 和平臺請大家自己測試, 錯誤在所難免, 還請指正.
下面是一些需要注意的問題:
4. To compile HelloWorld.java app in the default package in C:MyDir, use CD MyDir C:jdk1.3binJavac.exe -classpath . HelloWorld.java 5. To run a HelloWorld.class app, in the default package in C:MyDir, use CD MyDir C:jdk1.3binJava.exe -classpath . HelloWorld 6. To run a HelloWorld.class app, in the default package in a jar in C:MyDir, use CD MyDir C:jdk1.3binJava.exe -classpath HelloWorld.jar HelloWorld 7. To compile a HelloWorld.java app in C:MyPackage, in package MyPackage, use CD C:jdk1.3binJavac.exe -classpath . MyPackageHelloWorld.java 8. To run a HelloWorld.class app in C:MyPackage, in package MyPackage, use CD C:jdk1.3binJava.exe -classpath . MyPackage.HelloWorld 9. To run a HelloWorld.class app in C:MyPackage, in a jar in package MyPackage,, use CD MyDir C:jdk1.3binJava.exe -classpath HelloWorld.jar MyPackage.HelloWorl
(注: default package 指的是在程式中不指定任何包). 最後一個小小的建議, 把 sun 的 jdk tools documentation 好好地看一看,把 jdk 的那些工具 java javac javadoc jar javap jdb......好好用一用, 會有好處的. The Simplest Is The Best.
到此這篇關於Java 中 Class Path 和 Package的使用詳解的文章就介紹到這了,更多相關Java Class Path 和 Package內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45