Java程式設計思想:第六章:訪問控制
第六章:訪問控制
訪問控制(或者隱藏具體實現)與(最初的實現並不恰當)有關。
所有的優秀作者,包括哪些編寫軟體的程式設計師,都清楚著作的某些部分直至重新創作的時候才變得完美,有時甚至要反覆重寫幾次。如果你把一個程式碼段放到了某個位置,等過一會回頭再看時,有可能會發現優更好的方式去實現相同的功能。這正是重構的原則之一。重構及重寫程式碼,以使得它更好的可讀,更易理解, 並因此而更具可維護性。
但是,這種修改和完善程式碼的願望之下,也存在著巨大的壓力。通常總是會有一些消費者(客戶端)需要你的程式碼在某些方面保持不變。因此,你想改變程式碼,而他們卻像讓程式碼保持不變,因此而產生了在面向物件程式設計中需要考慮的一個基本問題:“如何把變動的事務與保持不變的事務區分開來”。
這對類庫而言尤為重要。該類的消費者必須依賴他所使用的那部分類庫,並且能夠知道如何類庫出現了新的版本,他們並不需要改寫程式碼。從另一個方面來說,類庫的開發者必須要有許可權進行修改和改進,並確保客戶程式碼不會因為這些改動而受到影響。
這一目標可以通過約束來達到,為了解決這個問題Java提供了訪問修飾符,以供類庫開發人員向客戶端程式設計師指明那些是可用的,那些是不可用的。
訪問許可權控制的等級,從最大許可權到最小許可權依次為:
public
protected
private
不過構建類庫的概念以及對於誰有權取用該類庫的構建的控制問題還是不完善的。其中仍舊存在著如何將構建捆綁到一個內聚的類庫單元中的問題。對於這一點,Java用關鍵字package加以控制,而訪問許可權修飾詞會因類是存在於一個相同的包,還是存在於一個單獨的包而受到影響。
6.1 包:庫單元
包內包含有一組類,它們在單一的名字空間之下被組織在一起。匯入一般使用:import java.util.*;
我們之所以匯入包,就是要提供一個管理名字空間的機制。所有類成員的名稱都是彼此隔離的。由於名字之間的衝突,Java對名稱空間進行了完全控制併為每個類建立唯一的識別符號組合就稱為了非常重要的事情。
當編寫一個Java原始碼檔案時,此檔案通常被稱為編譯單元。每一個編譯單元必須有一個字尾為.java,而在編譯單元內則可以有一個public類,該類的名稱必須與檔案的名字相同。每一個編譯單元只能有一個public類,否則編譯器就不會接受。如果編譯單元中有額外的類的話,那麼在包之外的世界是無法看到這些類的,就是因為他們不是public的,而且它們主要用來為主poblic類提供支援。
程式碼組織
當編譯一個.java檔案時,檔案中的每一個類都會有一個輸出檔案,而且該輸出檔案的名字與.java檔案中的每一個類的名稱相同。只是多了一個字尾名.class。因此,在編譯少量.java檔案之後,會得到大量的.class檔案。Java可執行程式是一組可以打包並解壓為一個java文件檔案的.class檔案。Java解析器負責這些檔案的查詢,裝載,解析。
類庫實際上是一組類檔案。其中每一個檔案都有一個public類,以及任意數量的非public類。因此,每個檔案都有一個構建。如果希望這些構建從屬於同一個群組,就可以使用關鍵字package。
如果使用package關鍵字,它必須是檔案中去除註釋外的第一句程式程式碼。在檔案起始處寫。如下:package.utils; 表示該編譯單元是名稱未utils的類庫的一部分。或者換種說話,你正在宣告該編譯單元中的public類名稱是位於utils名稱的保護傘下。任何想要使用該名稱的人都必須使用前面給出的選擇,指定全名或者與utils結合使用import。
身為一個類庫設計員,要記住,package和import關鍵字允許你做的,是將單一的全域性名字空間分隔開,使得無論多少人使用Internet以及Java開會編寫類,都不會出現名稱衝突問題。
建立獨一無二的包名
特定包的所有.class檔案都置於一個目錄下。也就是,作業系統的層次化的檔案系統結構來解決這個問題。這是Java解決混亂問題的解決方式。
將所有的檔案收入一個子目錄還可以解決另外兩個問題:
怎樣建立獨一無二的名稱以及怎樣查詢有可能隱藏於目錄結構中的某些類。一般package就是倒過來的域名,是獨一無二的。因此你的package名稱也是獨一無二的。所以不會出現衝突問題。
此技巧的第二部分是,把package名稱分解為你機子上的一個目錄。
Java解析器的執行過程如下:
首先,找出環境變數CLASSPATH。CLASSPATH包含一個或多麼目錄,用作查詢.class檔案的根目錄。從根目錄開始,解析器獲得包的名稱並將每一個句點替換成反斜槓,已從CLASSPATH根中產生一個路徑名稱,得到的路徑會與CLASSPaTH中各個不同的項相連線,解析器就在這個目錄中查詢與你所要建立的類名稱相符的.class檔案。
使用import引入時注意衝突哦。
用import改變行為
Java中沒有條件編譯,一般除錯時可以用到次動能。比如:建立兩個包dev,test。然後用import來切換。
對使用包的忠告
務必記住,無論何時建立包,都已經在給定包的名稱的時候隱含指定了目錄結構。這個包必須位於名稱所指定的目錄中,而該目錄必須在以CALASPATH開始的目錄中可以查詢到的。最初使用關鍵字package,可能會有一點不順,因為除非遵守“包的名字對應目錄路名”的規則,否則將會許多出乎意料的執行時資訊。
6.2 Java的訪問修飾符
6.3 介面與實現
訪問許可權的控制常被稱為具體實現的隱藏。把資料和方法包裝進類中,以及具體實現的隱藏,常共同被稱為封裝。其結果是一個同時帶有特徵和行為的資料型別。
出於兩個很重要的原因,訪問許可權控制將許可權的邊界劃在了資料型別的部分,如下:
要指定客戶端程式設計師可以使用和不可以使用的邊界。可以在結構中建立自己的內部機制,而不必擔心客戶端程式設計師會偶然地將內部機制當做是他們可以使用的介面的一部分。
將介面和具體實現分離。
為了清晰起見,可能會採用一種將public成員置於開頭,後面跟著protected,包訪問許可權和private成員的建立類的形式。這樣做的好處是使用者可以從頭讀起,首選閱讀對他而言最重要的部分,等到遇見作為內部實現細節的非public成員時停止閱讀。
6.4 類的訪問許可權
在Java中,訪問許可權修飾符也可以用於確定庫中的哪些類對於該庫的使用者是可用的。如果希望某個類可以為某個客戶端程式設計師所用,就可以通過public關鍵字作用於整個類的定義來達到目的。這樣做甚至可以控制客戶端程式設計師是否能建立一個該類的物件。
這裡有一些額外的限制:如下:
每個編譯單元都只能有一個public類。這表示,每個編譯單元都有單一的公共介面,用public類來表示。該介面可以按要求包含眾多的支援包訪問許可權的類。如果再某個編譯單元內有一個以上的public類,編譯器就給出錯資訊。
public類的名稱必須完全與含有編譯單元的檔名相匹配,包括大小寫。
雖然不是很常見,但編譯單元內完全不帶public類也是可能的。在這種情況下,可以隨意對檔案命名。
比如,內部類,去掉public就包許可權了,而且客戶端程式設計師也不會放到問,隱藏了細節。
類是不可以設定成private的,所以類而言只有兩種選擇:
包許可權
public
如果不希望別人對類訪問許可權,可以把構造器改成private來防止new出物件。但是有一個例外,就是你在該類的static成員內部可以建立。
正如前面所提到的,如果沒能為類訪問許可權指定一個訪問修飾符,它就會預設得到包訪問許可權。這意味著該類的物件可以由包內任何其他類來建立。但在包外則是不行的。然而,如果該類的某個static成員是public的,則客戶端程式設計師仍舊可以呼叫該static成員。儘管他們並不能生成該類的物件。