類路徑(classpath) java編譯器編譯.java文件和java虛擬機執行.class文件時的路徑和寫法不一樣。 在沒有設置任何classpath環境變數的情況下,javac可以編譯全路徑的.java文件。例如: 編譯後,在.java同路徑目錄下生成class文件。 預設java虛擬機要從c ...
類路徑(classpath)
java編譯器編譯.java文件和java虛擬機執行.class文件時的路徑和寫法不一樣。
在沒有設置任何classpath環境變數的情況下,javac可以編譯全路徑的.java文件。例如:
javac d:\myjava\HelloWorld.java
編譯後,在.java同路徑目錄下生成class文件。
預設java虛擬機要從classpath環境變數的路徑中搜索class文件去執行,對於java虛擬機來說,這不是類文件,而是類。它只有類路徑,而沒有文件系統路徑。而classpath環境變數正是為java虛擬機提供搜索類路徑的環境。註意,虛擬機不會遞歸搜索classpath定義的路徑。
也就是說,上面的java文件可以正確編譯,但卻不能執行。但如果將classpath設置為".;d:\myjava\",則java虛擬機將先從當前路徑搜索,再從d:\myjava下搜索class文件。
於是上面的HelloWorld.java編譯後,可以直接執行:
java HelloWorld
或者切換到d:\myjava目錄下,執行java HelloWorld
。
但下麵則是錯誤的方式,最後雖然能正確編譯NewDir.java,但在執行時,將搜索當前目錄(d:\myjava)下是否有NewDir.class,再搜索d:\myjava下是否有NewDir.class,但不會遞歸到子目錄newdir中去搜索class文件。
d:\
cd myjava
javac newdir\NewDir.java
java NewDir
再例如,在d:\myjava\hello下有兩個java源文件,它們的內容分別如下:
d:\myjava\hello
Cat.java
Dog.java
Cat.java
=========================
public class Cat {
}
Dog.java
=========================
public class Dog {
public static void main(String [] args) {
Cat c = new Cat();
}
}
其中Dog類中直接new了另一個文件中Cat類的對象,無論是編譯還是運行,這都是能成功的,因為javac編譯器編譯Dog.java時會自動從classpath指定的路徑下搜索Cat.class,正好這能搜索到,且該類又是public類,因此編譯成功。
總之,要明確的是javac編譯器搜索的是文件路徑,和環境變數classpath無關。而java虛擬機搜索的是類文件,嚴格地說是類,搜索路徑由環境變數classpath決定,且有先後順序。
更多的類路徑說明,見下麵的"包"。
包(package)
包是類的集合。在java源文件的第一行(不包括註釋行或空行)寫上package關鍵字並給定包名,即可將該類文件放到包中。
例如,d:\myjava\Cat.java文件:
package com.longshuai.home;
public class Cat {
public static void main(String[] args) {
System.out.println("com.longshuai.home.Cat");
}
}
這表示將Cat類放在com.longshuai.home包中。包應該以反轉後的功能變數名稱取名,防止包重名衝突,當然,這是非必須的。
對於沒有使用package指令的源文件,在編譯時其內的類都會預設當作"裸體類"。
java管理包的方法是以對應包名的目錄層次管理的,例如上面的com.longshuai.home包,應該將該class文件放在com/longshuai/home(如果是windows,則反斜線)下,即com/longshuai/home/Cat.class。
javac在編譯時從路徑上搜索文件。例如,將這個Cat.java放到com/longshuai/home下。執行時java虛擬機從classpath搜索要載入的類文件,而載入類的方式是使用"."連接各類名。所以編譯這個文件和java虛擬機執行這個文件時的方法分別是:
javac com/longshuai/home/Cat.java
java com.longshuai.home.Cat
註意,嵌套的包之間沒有任何關係,例如java.util包和java.util.jar包沒有任何依賴關係。
使用包中的類和導入包(import)
在某個java源文件中,無法直接使用其他文件中的類,除非要使用的這個類正好能被classpath的路徑搜索到。要引用非classpath下的其他類,只能將其添加到classpath或者裝入package中,然後引用包中的類。
引用包中類可以通過指定包名的方式引用來引用。例如:
com.longshuai.home.Cat c = new com.longshuai.home.Cat();
但顯然這很不方便。可以在java源文件的前幾行(但在package命令的後面)使用import指令導入需要使用的包中的類。例如導入Cat類,這樣就可以直接使用該類了:
import com.longshuai.home.Cat;
Cat c = new Cat();
導入包時可以在尾部使用星號"*"通配導入的所有類,只能在尾部使用"*",因為"*"匹配的是類名,而不是包名。也因此,不能在非結尾處使用"*"號來表示導入其他包中的類,例如:
import com.longshuai.home.*; //導入com.longshuai.home包中的所有類
import com.longshuai.*; //導入com.longshuai包中所有類,但不會導入com.longshuai.home中的類,
//因為雖然層次之間有嵌套,但這些包沒有任何關係
import com.*.*; //這是錯誤的寫法
如果導入的包中有同名的類,則在引用同名類的時候會產生衝突錯誤,例如java.util和java.sql包中都有Date類,
import java.util.*;
import java.sql.*;
public class Test {
public static void main(String [] args) {
Date today = new Date();
}
}
編譯:
javac Test.java
Test.java:11: 錯誤: 對Date的引用不明確
Date today = new Date();
^
java.sql 中的類 java.sql.Date 和 java.util 中的類 java.util.Date 都匹配
Test.java:11: 錯誤: 對Date的引用不明確
Date today = new Date();
^
java.sql 中的類 java.sql.Date 和 java.util 中的類 java.util.Date 都匹配
2 個錯誤
這時可以顯式導入Date類,或者在使用Date類的時候指定包名。也就是說下麵兩種方法都正確:
//方法一:
import java.util.*;
import java.sql.*;
import java.util.Date;
//方法二:
import java.util.*;
import java.sql.*;
public class Test {
public static void main(String [] args) {
java.util.Date today = new java.util.Date();
}
}
除了可以導入包中的類,還可以靜態導入包中類中的靜態方法和靜態變數,只需加上static關鍵字並指定要導入的內容即可。例如:
import static java.lang.System.*;
import static java.lang.System.out;
靜態導入方法後,就可以省略首碼,例如:
import static java.lang.System.out;
public class ClassName {
public static void main() {
out.println("HelloWorld");//等價於System.out.println("HelloWorld");
}
}
將package歸檔成jar包
java虛擬機可以直接識別jar包。可以將package名稱對應的路徑使用jar命令歸檔成jar包。jar命令使用說明如下:
jar
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files .
..
選項:
-c 創建新檔案
-t 列出檔案目錄
-x 從檔案中提取指定的 (或所有) 文件
-u 更新現有檔案
-v 在標準輸出中生成詳細輸出
-f 指定檔案文件名
-m 包含指定清單文件中的清單信息
-n 創建新檔案後執行 Pack200 規範化
-e 為捆綁到可執行 jar 文件的獨立應用程式
指定應用程式入口點
-0 僅存儲; 不使用任何 ZIP 壓縮
-P 保留文件名中的前導 '/' (絕對路徑) 和 ".." (父目錄) 組件
-M 不創建條目的清單文件
-i 為指定的 jar 文件生成索引信息
-C 更改為指定的目錄並包含以下文件
如果任何文件為目錄, 則對其進行遞歸處理。
清單文件名, 檔案文件名和入口點名稱的指定順序
與 'm', 'f' 和 'e' 標記的指定順序相同。
例如,將當前目錄下的a.class和b.class打包到test.jar中:
jar cvf test.jar a.class b.class
查看jar包中的文件列表,會遞歸顯示:
jar -tf test.jar
META-INF/
META-INF/MANIFEST.MF
jiecheng.class
例如,將com目錄歸檔到d:\dp.jar中。
jar cvf d:\dp.jar com/
已添加清單
正在添加: com/(輸入 = 0) (輸出 = 0)(存儲了 0%)
正在添加: com/longshuai/(輸入 = 0) (輸出 = 0)(存儲了 0%)
正在添加: com/longshuai/home/(輸入 = 0) (輸出 = 0)(存儲了 0%)
正在添加: com/longshuai/home/Bird.class(輸入 = 420) (輸出 = 291)(壓縮了 30%)
正在添加: com/longshuai/home/Bird.java(輸入 = 136) (輸出 = 100)(壓縮了 26%)
正在添加: com/longshuai/home/Cat.class(輸入 = 417) (輸出 = 289)(壓縮了 30%)
正在添加: com/longshuai/home/Cat.java(輸入 = 134) (輸出 = 99)(壓縮了 26%)
有了jar文件,就可以直接設置classpath的路徑為jar文件名,這樣在搜索類文件時就會直接從jar文件內搜索。例如classpath設置為:
.;d:\myjava;d:\dp.jar
類搜索機制
在java虛擬機搜索類文件時,除了classpath環境變數指定的路徑,還會先搜索兩個預設的路徑:jre/lib和jre/lib/ext下的jar文件中似乎否有待搜索的類。
例如,當classpath設置為".;d:\myjava;d:\myjar.jar"時,要搜索com.longshuai.com.Cat類文件:
(a).先搜索jre/lib和jre/lib/ext下的jar文件;
(b).再搜索當前目錄下是否有com\longshuai\com\Cat.class;
(c).再搜索d:\myjava\Cat.class;
(d).搜索d:\myjar.jar文件中是否有com.longshuai.com.Cat類。
如果在某個java源文件中引用了某個類,則在編譯時,將通過以下幾種方式判斷該類是否合理有效:
(1).搜索導入的包類中是否包含該類。
(2).搜索隱式導入的java.lang包,該包是預設導入的。
(3).當前文件中是否定義了該類。
(4).按照類路徑的搜索規則((a)-(d))搜索其中是否有該類。
繼承
類與類之間能體現"什麼是什麼"的語義邏輯,就能實現類的繼承。例如,貓是動物,那麼貓類可以繼承動物類,而貓類稱為子類,動物類稱為父類。
子類繼承父類後,子類就具有了父類所有的成員,包括成員變數、方法。實際上在記憶體中,new子類對象時,heap中劃分了一部分區域存放從父類繼承來的屬性。例如,new parent得到的區域A,new child得到的區域B,區域A在區域B中。
子對象中之所以包含父對象,是因為在new子對象的時候,首先調用子類構造方法構造子對象,在開始構造子對象的時候又首先調用父類構造方法構造父對象。也就是說,在形成子對象之前,總是先形成父對象,然後再慢慢的補充子對象中自有的屬性。具體內容見"繼承時構造方法的重寫super()"。
子類不僅具有父類的成員,還具有自己獨有的成員,例如有自己的方法、自己的成員變數。子類、父類中的成員名稱不同時這很容易理解,但它們也可能是同名的。如果子類中有和父類繼承的同名方法,例如父類有eat()方法,子類也有eat()方法,則這可能是方法的重寫(見下文)。如果子類中的成員變數和父類的成員變數同名,則它們是相互獨立的,例如父類有name屬性,子類還自己定義了一個name屬性,這是允許的,因為可以分別使用this和super來調用它們。
繼承類時使用extends關鍵字。繼承時,java只允許從一個父類繼承。
class Person {
String name;
int age;
void eat() { System.out.println("eating...");}
void sleep() {System.out.println("sleep...");}
}
class Student extends Person {
int studentID;
Student(int id,String name,int age) {
this.name = name;
this.age = age;
this.studentID = id;
}
void study() {System.out.println("studing...");}
}
public class Inherit {
public static void main(String[] args) {
Student s1 = new Student(1,"Malongshuai",23);
System.out.println(s1.studentID+","+s1.name+","+s1.age);
s1.eat();
s1.sleep();
s1.study();
}
}
註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!