package的用法

例如聲明一個名爲pack1的包名,package pack1;好了下面我們就利用範例來說明package的使用方法,及其編譯運行的方法。

例一

假設在d盤下的新建一個文件夾(pack1),在pack1文件夾內包含兩個java程序一個是Showmethod.java和Testshow.java;
Showmethod.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
packagepack1;//在這裏把Showmethod類納入到pack1包內
classShowmethod{
publicvoidshow(){
System.out.println("I'mashowmethod()ofShowmethodclass");
}
}
Testshow.java;
packagepack1;//在這裏把Testshow類也納入到pack1包內
publicclassTestshow{
publicstaticvoidmain(Stringargs[]){
Showmethodsm=newShowmethod();
sm.show();
}
}

例二

訪問不在同一個包內的類,
在D盤分別建立pack1,pack2兩個目錄,
建立好之後將Showmethod.java放到pack1包內,
Showmethod.java的代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
packagepack1;
publicclassShowmethod{
publicvoidshow(){
System.out.println("I'mashowmethod()ofShowmethodclass");
}
}
將Testshow.java放到pack2包內
Testshow.java的代碼如下:
packagepack2;
publicclassTestshow{
publicstaticvoidmain(Stringargs[]){
pack1.Showmethodsm=newpack1.Showmethod();
sm.show();
}
}
由於此類需要調用位於pack1包中的Showmethod類的show方法,所以這裏pack1.Showmethod sm=new pack1.Showmethod();這樣寫,還可以使用import 語句將pack1包內的Showmethod方法導入進來,這樣這個地方pack1.Showmethod sm=new pack1.Showmethod();所以可以這樣寫Showmethod sm=new Showmethod()。

編譯指南

編輯
編譯這兩個位於同一個包內的java程序
1:javac pack1\Showmethod.java
2:javac pack1\Testshow.java
這就是編譯的方法 pack1是包名,包名後面跟的就是我們要編譯的那個類名,編譯好後pack1目錄下會生成兩個class文件
運行方法:
java pack1.Testshow
注意運行時要指出包名,包名後有一個點"."然後是類名。
在訪問不同 package裏的類時,被訪問的類必須被聲明爲public(就是公有類型的),否則在編譯會報告錯誤。
編譯方法"
1javac pack1\Showmethod.java
2: javac pack2\Testshow.java
編譯好之後pack1,pack2目錄下會分別產生一個.class文件
運行方法:
java pack2.Testshow
使用import導入package。
————————————————————————————————————————
我的一點想法:
Java中的類按照package來進行存放和管理。類似於linux系統的目錄樹,好比java用來組織文件的虛擬文件系統;而.java源文件則是實體文件系統,因此它的編譯要嚴格按照目錄結構來進行,比如我的java工作目錄爲D:\workspace,在workspace下建立了名字爲chapter的包,其中1.java在chapter包裏面,則cd workspace後,輸入javac 1.java會報錯找不到文件,javac chapter\1.java則會編譯成功,同樣cd chapter; javac 1.java 也會成功。這印證了在編譯時必須嚴格按照木結構以使得編譯器能夠找到.java文件的位置。
而.class文件的運行則是按照package規定的虛擬目錄結構來管理的。比如上面編譯成功的1.class文件會在chapter中生成,cd chapter進去chapter目錄後,輸入java 1,報錯找不到主類,而輸入java chapter.1則會成功運行(前提是D:\workspace已經設置環境變量加入類路徑),同樣在workspace目錄下輸入java chapter.1也會成功運行。
假如workspace下有兩個文件1.java和2.java,其中2.java含有主類,且它使用了1.java定義的類,javac 1.java會編譯通過,但生成的1.class在workspace中,當再編譯2.java時,輸入javac 2.java會報錯找不到1.class,分析這個過程,編譯器首先通過類路徑指定的目錄順序逐個搜索1.class,按說應當在workspace下找到1.class並進行匹配,而實際情況卻是編譯器沒有找到1.class,這說明1.class由於第一句package chapter的原因使得它的虛擬路徑並不屬於D:\workspace\,因此編譯器是找不到的,而把1.class放入chapter裏面則可以編譯成功。

————————————————————————————————————————————————————————
轉載:
java應用程序文件
這個說法是我自己抽象出來的,指的是一般的組織在package中的所有文件。
大體分成這三種:
1,java程序源文件,擴展名爲.java。
2,編譯好的java類文件,擴展名爲.class。
3,其他文件,除了以上的,就是資源文件。
例如圖片文件,xml文件,mp3文件
等等等等都可以組織在package之中。(你當然也可以把一部電影放在package裏面,當然
不提倡這樣做)

package幹嗎用?
package就是組織文件的一種方式。
最大的作用就是組織java類文件。
成千上萬的class文件,總難免重名吧,即便不重名,那麼多你記得住麼?當然要分門別類
的組織起來方便你隨時取用吧,這個就是package最大的作用。

package是個什麼東西?
package本身是一個比較抽象的邏輯概念,它的宗旨是將東西組織在一棵樹裏面,就好像
linux的文件系統一樣,它有一個根,然後從根開始有目錄和文件,然後目錄中也可以有
目錄。而實現的時候是怎樣的呢?是利用文件系統的目錄結構或者利用壓縮文件中的目錄
結構來組織的。

package怎麼實現的?
首先講其最重要的應用——組織源代碼。
我們知道java源代碼的基本單元是一個.java文本文件。
一個.java文件可以包含一個public類和無數包級類,
默認什麼訪問修飾都沒有的類就是一個包級類。這種類只能在本package中使用。
當然了,類當中還可以有類,名喚內隱類,內隱類的訪問修飾就可以是protected
或者甚至是private了。
好,那麼何謂本package呢?
當你在源代碼的最開頭(當然,註釋可以放在更開頭的位置)不寫package語句的話,那麼
當前編譯單元中的所有編譯出來的類就只能夠放在package的"/"上,這種package我們叫做
defaultpackage。一般我們寫個HelloWorld或者簡單調幾個api實現個小功能的話,一般
採用這種“不打包”似的default package就可以了。
但是,當你的類開始氾濫起來的時候,你就必須考慮給類打個包放在某個package中了。
方法很簡單,在所有代碼開始之前寫一句
package 包名;
即可。
包名是用"."間隔的字符串,一般能夠當作文件名的字符串就能用到這裏來。
比如
package aaa.bbb.ccc;
意味着這個編譯單元所編譯出來的class都必須放在/aaa/bbb/ccc這個目錄之下。
否則這種類就無法正常使用,正是這種機制保證了類的嚴格組織。
實際上,當你在編譯一些有package語句的編譯單元的時候,如果你使用的是命令行的話,
應該使用-d參數讓編譯器自己生成目錄結構,實際上你的.java就應該放在這種目錄結構
之中。我舉個非常簡單的例子,有個人寫了這麼兩個.java文件。
/*   A.java */
package aaa.bbb.ccc;
public class A{
    B b=newB();
}
/* B.java*/
package aaa.bbb.ccc;
public class B{}
然後這個人就把這兩個文件就放在當前目錄之下,然後javacA.java,
然後傻眼。

說找不到B,怎麼找不到呢?
爲什麼找不到呢,不是在一個package裏面嗎?

對,是在一個package裏面,但是B.java必須放在當前目錄/aaa/bbb/ccc下面纔可用,
當javac編譯A.java的時候,它發現當前所在的包是aaa.bbb.ccc,然後需要一個B類,
沒有B.class,就要去找B.java,而B.java的package語句和自己所在目錄結構不符,沒法
使用,所以就傻眼了。

這個人就知道了把B.java放到當前目錄/aaa/bbb/ccc裏面,嘿,編譯通過了。
沒錯,生成了A.class,但是A.class還是不可用的,因爲它不在它應該在的地方。
我們又必須將它放到它應該在的地方,它才能好好運作。

那你說我把A.java也放在當前目錄/aaa/bbb/ccc裏面,然後
javac aaa.bbb.ccc.A.java
漂亮吧?
不行,爲什麼?因爲javac接受的參數是一個文件系統的文件名,javac命令行中的參數
只能是一個完整的路徑名。
那我把A.java放在當前目錄/aaa/bbb/ccc裏面,然後
javac aaa/bbb/ccc/A.java
這下好了吧,恩,差不多,但是生成的class還是直接丟在了源代碼目錄之下

最好是
javac -d bin aaa/bbb/ccc/A.java
這樣就會在當前目錄的bin目錄下看到完整的目錄結構以及放置妥當的class文件。

package與classpath不得不說的事
講了半天還是覺得不太清楚package的"/"到底在哪裏,在當前目錄嗎?
如果不知道package的"/"在哪裏,那那些package中的文件在哪裏就更無從得知了。
回答很簡單,package的"/"在classpath中。
頭痛了,package沒有搞懂的,classpath一般也不會很懂。
沒事,classpath說白了就是一堆path,這些該死的path有的指向一個目錄,有的指向一個
文件(只能是zip或者jar兩種,當然更保險的話還是用jar比較好)。
對於java虛擬機而言,它並不想過問這些path的八卦,它所做的是一視同仁!
它要的是所有這些path中的內容,對,對它而言,它不在乎內容是來自一個目錄還是來自
一個文件,總而言之,所有的這些東西在他眼裏都一樣。就好像它空出一塊地方,把所有
的文件解壓,把所有的目錄中的內容保留目錄結構的送入相應位置,形成一套它自己感
興趣的package組織方式。它要的就是這種方式來訪問。
所以我可以在a.jar中有一個目錄結構aaa/bbb/ccc/A.class,然後在b.jar中也有一個同樣
的目錄結構aaa/bbb/ccc/B.class。當我把這兩個文件都放在classpath中的時候,
對虛擬機而言,這個A.class和這個B.class就是放在同一個package中的。
前面的那個實踐也透視了這一點,實際上編譯A.java的時候,是在classpath中尋找class
文件的,找不到,則會在sourcepath中尋找其源代碼文件(不設定的話,sourcepath就是等
於classpath),當我們把B.java放在當前目錄中合適的目錄結構中的時候,它就會找到
這個文件並將它編譯,然後進行使用。因爲當前目錄"."是在classpath中的。
原則就是,對java而言,它是忽略文件系統細節的,它要求的只是自己的classpath
中按照約定存放的資源。

(以上classpath對應的classloader是app classloader)

import幹嗎用的?
java幾乎所有的api都是用java語言本身寫成的,是的,那些你所經常使用的String,
HashSet,HashMap等等這些類都是用java語言寫成的。
這些類的製作是我們也可以來做的事情,他們同樣是寫出一個個java文件,打上不同的
package,然後編譯好,最後壓縮一下,成爲jre裏面的dt.jar之類的文件,變成了我們常
常調用的庫。
有的人以爲,當我們寫下importjava.util.*;之前,我們的這些庫就是“不可達”的,
而當我們寫下了importjava.util.*之後,我們所需要的那個庫文件就會被某種不知道的
機制加入到我們的工程。
錯!這些人是純粹打死不看書的想當然類型,只在乎自己的意象,不在乎其所以然。

不管你寫還是不寫import語句,對當前外部的編譯和執行環境都不會有任何的影響。
即便你不寫import java.util.*;
你照樣可以用java.util.HashMap來調用系統api,根本不用做更多的事情。
這裏我不得不再提一次,不要把dt.jar放到CLASSPATH環境變量中,它已經在classpath中
了,而且此舉常常讓初學者丟掉了默認在classpath中的"."當前目錄,讓他們一番熱情學
java寫helloworld的火焰被一盆水撲滅。

import有啥用的呢?
它只不過是一種讓你少打點字的方法,就跟c++的名稱空間的using語句功能一樣,就是暴
露那個package下面所有的類,寫代碼的時候就可以直接寫類名,而不用寫下完整的
包名.類名,僅此而已。
不得不提的是,*僅僅代表類名,不負責以下更深層次中各級目錄中的東西。
importjava.*;實際上沒有暴露任何類,因爲沒有類是打在這個包裏面的。它沒有
暴露java.util.*,也沒有暴露java.sql.*等等,它僅僅暴露了java目錄下面所有的類。

打包成jar文件
一般的打包成jar文件,除了將目錄結構壓縮存儲在一個zip格式但是擴展名比較畸形的
文件中之外沒有做任何事情。
jar文件可以用任何能夠解壓zip的工具來解壓,如果你討厭使用jar命令行,你甚至可以
直接使用winrar來壓縮,然後改個名字即可。
當然了,jar文件也有別的用處,比如“可直接執行的jar”。
“可直接執行的jar”跟“不可直接執行的jar”的區別僅僅在於它多了一個配置文件在
jar文件中,這個文件中指出了一個完整的類名,告訴java.exe或者javaw.exe,main方法
這個類裏面而已。
欲知道更多關於jar文件的用法,請參考精華區。

從實踐到理論

剛纔我用一個非常簡單但是非常完整的例子給大家演示了java的package機制。
爲什麼以前腦海裏面那麼簡單的javac會搞得這麼複雜呢?

實際上它本就這麼複雜,
並不是一個.java,一行javac一個當前目錄中的class這麼簡單。

首先我要打破一些東西,然後纔好建立一些東西。
javac並非只是給一個.java編譯一個class的。javac自帶有make機制,也就是說,如果在
javac的參數中java文件使用到的任何類,javac首先會去找尋這個類的class文件存在與否
,如果不存在,就會在sourcepath中找尋源代碼文件來編譯。

什麼是sourcepath呢?sourcepath是javac的一個參數,如果你不加指定,那麼sourcepath
就是classpath。

比如我們裝好jdk之後,我說過不要設定classpath環境變量,因爲大部分人一旦設定了
classpath,不是多此一舉把什麼dt.jar放進去。(我可以好好打擊你一下,告訴你一個可
悲的事實——jre永遠不會從你這個classpath中去尋找dt.jar。你完全是徒勞的!)就是
把"."搞不見了,搞得是冷水一盆盆的往自己身上潑,腦袋一點點的漲大。

不要設!classpath沒有你想象中那麼普適和強大,它只是命令行的簡化替代品。
你不設的話它就是"."。


回到sourcepath,sourcepath指的是你源代碼樹的存放地點。
爲什麼是源代碼樹?而不是一個目錄的平板源代碼呢?
請大家將原本腦海中C的編譯過程完全砸掉!
java完全不同,java沒有頭文件,每個.java都是要放在源代碼樹中的。
那麼這顆樹是怎麼組織的呢?
對了,就是package語句。
比如寫了package com.lightning;
那麼這個.java就必須放在源代碼樹根\的com\lighting\之下才行。

很多浮躁的初學者被default打包方式寵壞了。自我爲中心,以爲java就是一套庫,自己寫
的時候最多import進來就行了,代碼從不打包,直接javac,直接java,多麼方便。
孰不知自己寫的這個.java也不過是java大平臺的其中一個小單元而已。如果不打包,
我寫一個Point,你寫一個Point,甚至更有甚者敢於給自己的類起名叫String等等。
全部都在平板式的目錄中,那jre該選哪一個?

一旦要使用package語句,就要使用代碼樹結構,當然,你要直接javac,也行。
不過javac出來的這個class要放在符合package結構的目錄中才能發揮正常作用,否則就是
廢物一坨。
而且,如果你這個.java還用到其它自己寫的有package語句的.java,那這個方法就回天乏
術了。

按照sun的想象,應該是寫好的.java放在符合package結構的目錄中,package語句保證了
正確放置,如果目錄位置和package語句中指示的不同,則會出錯。

所以按照剛纔我們的那種package寫法,我們必然要將那幾個.java文件放入相應的目錄中
才能讓javac順利找到他們來make。

有人說javac可不可以像java那樣 java aaa.bbb.c...java?
不可以
javac中的那個.java文件參數必須是一個文件系統的路徑文件名形式。
然後如果用到其它的.java,javac會根據目前的sourcepath出發尋找目錄結構中的那些
java文件。

當然了,既然打了包,在使用的時候。
要麼寫全名——包名.類名
或者使用import
不得不提的是,import就好比c++的using,它不負責做文件操作,它只是方便你寫代碼。

另外import語句中的*代表的是類名,不代表包名片斷。
你import com.*;
編譯器仍然找不到com.lightning中的任何類。
反之亦然。
就好象你告訴編譯器,我這裏面要用到姓諸葛的人。
那麼姓諸的人當然編譯器不會認爲也包含在內。


所以,如果程序一旦寫到一定規模。
就不得不使用ant來管理這些。
或者使用IDE,否則jdk就真的變成了java developer killer。

但是對於初學者,在瞭解爲什麼會有ant之類的之前,還是要體會一下使用
jdk的艱辛。

這個和以前在unix上開發的人用gcc命令行到後來使用make之後使用ide
之類的時候的體會是類似的。

最後,javac的-d參數是指示編譯出來的class文件放在哪裏的,如果你不指定的話,它們
和.java混在一起。當然也符合目錄結構

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章