計算機程序的思惟邏輯 (22) - 代碼的組織機制

本系列文章經補充和完善,已修訂整理成書《Java編程的邏輯》(馬俊昌著),由機械工業出版社華章分社出版,於2018年1月上市熱銷,讀者好評如潮!各大網店和書店有售,歡迎購買:京東自營連接 html

使用任何語言進行編程都有一個相似的問題,那就是如何組織代碼,具體來講,如何避免命名衝突?如何合理組織各類源文件?如何使用第三方庫?各類代碼和依賴庫如何編譯鏈接爲一個完整的程序?java

本節就來討論Java中的解決機制,具體包括包、jar包、程序的編譯與鏈接,從包開始。apache

包的概念

使用任何語言進行編程都有一個相同的問題,就是命名衝突,程序通常不全是一我的寫的,會調用系統提供的代碼、第三方庫中的代碼、項目中其餘人寫的代碼等,不一樣的人就不一樣的目的可能定義一樣的類名/接口名,Java中解決這個問題的方法就是包。編程

即便代碼都是一我的寫的,將不少個關係不太大的類和接口都放在一塊兒,也不便於理解和維護,Java中組織類和接口的方式也是包。微信

包是一個比較容易理解的概念,相似於電腦中的文件夾,正如咱們在電腦中管理文件,文件放在文件夾中同樣,類和接口放在包中,爲便於組織,文件夾通常是一個層次結構,包也相似。模塊化

包有包名,這個名稱以頓號(.)分隔表示層次結構。好比說,咱們以前經常使用的String類,就位於包java.lang下,其中java是上層包名, lang是下層包名,帶完整包名的類名稱爲其徹底限定名,好比String類的徹底限定名爲java.lang.String。Java API中全部的類和接口都位於包java或javax下,java是標準包,javax是擴展包。工具

接下來,咱們討論包的細節,從聲明類所在的包開始。spa

聲明類所在的包

語法

咱們以前定義類的時候沒有定義其所在的包,默認狀況下,類位於默認包下,使用默認包是不建議的,文章中使用默認包只是簡單起見。3d

定義類的時候,應該先使用關鍵字package,聲明其包名,以下所示:code

package shuo.laoma;

public class Hello {
    //類的定義
}
複製代碼

以上聲明類Hello的包名爲shuo.laoma,包聲明語句應該位於源代碼的最前面,前面不能有註釋外的其餘語句。

包名和文件目錄結構必須匹配,若是源文件的根目錄爲 E:\src\,則上面的Hello類對應的文件Hello.java,其全路徑就應該是E:\src\shuo\laoma\Hello.java。若是不匹配,Java會提示編譯錯誤。

命名衝突

爲避免命名衝突,Java中命名包名的一個慣例是使用域名做爲前綴,由於域名是惟一的,通常按照域名的反序來定義包名,好比,域名是:apache.org,包名就以org.apache開頭。

沒有域名的,也不要緊,使用一個其餘代碼不太會用的包名便可,好比本文使用的"shuo.laoma",表示"老馬說編程"中的例子。

若是代碼須要公開給其餘人用,最好有一個域名以確保惟一性,若是隻是內部使用,則確保內部沒有其餘代碼使用該包名便可。

組織代碼

除了避免命名衝突,包也是一種方便組織代碼的機制,通常而言,同一個項目下的全部代碼,都有一個相同的包前綴,這個前綴是惟一的,不會與其餘代碼重名,在項目內部,根據不一樣目的再細分爲子包,子包可能又會分爲子包,造成層次結構,內部實現通常位於比較底層的包。

包能夠方便模塊化開發,不一樣功能能夠位於不一樣包內,不一樣開發人員負責不一樣的包。包也能夠方便封裝,供外部使用的類能夠放在包的上層,而內部的實現細節則能夠放在比較底層的子包內。

經過包使用類

同一個包下的類之間互相引用是不須要包名的,能夠直接使用。但若是類不在同一個包內,則必需要知道其所在的包,使用有兩種方式,一種是經過類的徹底限定名,另一種是將用到的類引入到當前類。

只有一個例外,java.lang包下的類能夠直接使用,不須要引入,也不須要使用徹底限定名,好比String類,System類,其餘包內的類則不行。

好比說,使用Arrays類中的sort方法,經過徹底限定名,能夠這樣使用:

int[] arr = new int[]{1,4,2,3};
java.util.Arrays.sort(arr);
System.out.println(java.util.Arrays.toString(arr));
複製代碼

顯然,這樣比較囉嗦,另一種就是將該類引入到當前類,引入的關鍵字是import,import須要放在package定義以後,類定義以前,以下所示:

package shuo.laoma;
import java.util.Arrays;

public class Hello {
    public static void main(String[] args) {
        int[] arr = new int[]{1,4,2,3};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
複製代碼

import時,能夠一次將某個包下的全部類引入,語法是使用.*,好比,將java.util包下的全部類引入,語法是:import java.util.*,須要注意的是,這個引入不能遞歸,它只會引入java.util包下的直接類,而不會引入java.util下嵌套包內的類,好比,不會引入包java.util.zip下面的類。試圖嵌套引入的形式也是無效的,如import java.util.*.*。

在一個類內,對其餘類的引用必須是惟一肯定的,不能有重名的類,若是有,則經過import只能引入其中的一個類,其餘同名的類則必需要使用徹底限定名。

引入類是一個比較繁瑣的工做,不過,大多數Java開發環境都提供工具自動作這件事,好比,在Eclipse中,經過菜單"Source->Organize Imports"或對應的快捷鍵ctrl+shift+O就能夠自動管理引入類。

包範圍可見性

前面幾節咱們介紹過,對於類、變量和方法,均可以有一個可見性修飾符,public/private/protected,而上節,咱們提到能夠不寫修飾符。若是什麼修飾符都不寫,它的可見性範圍就是同一個包內,同一個包內的其餘類能夠訪問,而其餘包內的類則不能夠訪問。

須要說明的是,同一個包指的是同一個直接包,子包下的類並不能訪問,好比說,類shuo.laoma.Hello和shuo.laoma.inner.Test,其所在的包shuo.laoma和shuo.laoma.inner是兩個徹底獨立的包,並無邏輯上的聯繫,Hello類和Test類不能互相訪問對方的包可見性方法和屬性。

另外,須要說明的是protected修飾符,protected可見性包括包可見性,也就是說,聲明爲protected,不只代表子類能夠訪問,還代表同一個包內的其餘類能夠訪問,即便這些類不是子類也能夠。

總結來講,可見性範圍從小到大是:

private < 默認(包) < protected < public

jar包

爲方便使用第三方代碼,也爲了方便咱們寫的代碼給其餘人使用,各類程序語言大多有打包的概念,打包的通常不是源代碼,而是編譯後的代碼,打包將多個編譯後的文件打包爲一個文件,方便其餘程序調用。

在Java中,編譯後的一個或多個包的Java class文件能夠打包爲一個文件,Java中打包命令爲jar,打包後的文件後綴爲.jar,通常稱之爲jar包。

可使用以下方式打包,首先到編譯後的java class文件根目錄,而後運行以下命令打包:

jar -cvf <包名>.jar <最上層包名>

好比,對前面介紹的類打包,若是Hello.class位於E:\bin\shuo\laoma\Hello.class,則能夠到目錄 E:\bin下,而後運行:

jar -cvf hello.jar shuo

hello.jar就是jar包,jar包其實就是一個壓縮文件,可使用解壓縮工具打開。

Java類庫、第三方類庫都是以jar包形式提供的。如何使用jar包呢?將其加入到類路徑(classpath)中便可。類路徑是什麼呢?

程序的編譯與鏈接

從Java源代碼到運行的程序,有編譯和鏈接兩個步驟。編譯是將源代碼文件變成一種字節碼,後綴是.class的文件,這個工做通常是由javac這個命令完成的。鏈接是在運行時動態執行的,.class文件不能直接運行,運行的是Java虛擬機,虛擬機聽起來比較抽象,執行的就是java這個命令,這個命令解析.class文件,轉換爲機器能識別的二進制代碼,而後運行,所謂鏈接就是根據引用到的類加載相應的字節碼並執行。

Java編譯和運行時,都須要以參數指定一個classpath,即類路徑。類路徑能夠有多個,對於直接的class文件,路徑是class文件的根目錄,對於jar包,路徑是jar包的完整名稱(包括路徑和jar包名),在Windows系統中,多個路徑用分號;分隔,在其餘系統中,以冒號:分隔。

在Java源代碼編譯時,Java編譯器會肯定引用的每一個類的徹底限定名,肯定的方式是根據import語句和classpath。若是import的是徹底限定類名,則能夠直接比較並肯定。若是是模糊導入(import帶.*),則根據classpath找對應父包,再在父包下尋找是否有對應的類。若是多個模糊導入的包下都有一樣的類名,則Java會提示編譯錯誤,此時應該明確指定import哪一個類。

Java運行時,會根據類的徹底限定名尋找並加載類,尋找的方式就是在類路徑中尋找,若是是class文件的根目錄,則直接查看是否有對應的子目錄及文件,若是是jar文件,則首先在內存中解壓文件,而後再查看是否有對應的類。

總結來講,import是編譯時概念,用於肯定徹底限定名,在運行時,只根據徹底限定名尋找並加載類,編譯和運行時都依賴類路徑,類路徑中的jar文件會被解壓縮用於尋找和加載類。

小結

本節介紹了Java中代碼組織的機制,包和jar包,以及程序的編譯和鏈接。將類和接口放在合適的具備層次結構的包內,避免命名衝突,代碼能夠更爲清晰,便於實現封裝和模塊化開發,經過jar包使用第三方代碼,將自身代碼打包爲jar包供其餘程序使用,這些都是解決複雜問題所必需的。

咱們一直在說,程序主要就是對數據的操做,爲表示和操做數據,咱們介紹了基本類型,類以及接口,下節,咱們介紹Java中表示和操做一種特殊數據的機制 - 枚舉


未完待續,查看最新文章,敬請關注微信公衆號「老馬說編程」(掃描下方二維碼),深刻淺出,老馬和你一塊兒探索Java編程及計算機技術的本質。用心原創,保留全部版權。

相關文章
相關標籤/搜索