在Java中,亦可用訪問指示符判斷出一個庫內的哪些類可由那個庫的用戶使用。若想一個類能由客戶程序員調用,可在類主體的起始花括號前面某處放置一個public關鍵字。它控制著客戶程序員是否能夠創建屬於這個類的一個對象。
為控制一個類的訪問,指示符必須在關鍵字class之前出現。所以我們能夠使用:
public class Widget {
也就是說,假若我們的庫名是mylib,那麼所有客戶程序員都能訪問Widget——通過下述語句:
import mylib.Widget;
或者
import mylib.*;
然而,我們同時還要注意到一些額外的限制:
(1) 每個編譯單元(文件)都只能有一個public類。每個編譯單元有一個公共接口的概念是由那個公共類表達出來的。根據自己的需要,它可擁有任意多個提供支撐的“友好”類。但若在一個編譯單元裡使用了多個public類,編譯器就會向我們提示一條出錯消息。
(2) public類的名字必須與包含了編譯單元的那個文件的名字完全相符,甚至包括它的大小寫形式。所以對於Widget來說,文件的名字必須是Widget.java,而不應是widget.java或者WIDGET.java。同樣地,如果出現不符,就會報告一個編譯期錯誤。
(3) 可能(但並常見)有一個編譯單元根本沒有任何公共類。此時,可按自己的意願任意指定文件名。
如果已經獲得了mylib內部的一個類,准備用它完成由Widget或者mylib內部的其他某些public類執行的任務,此時又會出現什麼情況呢?我們不希望花費力氣為客戶程序員編制文檔,並感覺以後某個時候也許會進行大手筆的修改,並將自己的類一起刪掉,換成另一個不同的類。為獲得這種靈活處理的能力,需要保證沒有客戶程序員能夠依賴自己隱藏於mylib內部的特定實施細節。為達到這個目的,只需將public關鍵字從類中剔除即可,這樣便把類變成了“友好的”(類僅能在包內使用)。
注意不可將類設成private(那樣會使除類之外的其他東西都不能訪問它),也不能設成protected(注釋④)。因此,我們現在對於類的訪問只有兩個選擇:“友好的”或者public。若不願其他任何人訪問那個類,可將所有構建器設為private。這樣一來,在類的一個static成員內部,除自己之外的其他所有人都無法創建屬於那個類的一個對象(注釋⑤)。如下例所示:
//: Lunch.java // Demonstrates class access specifiers. // Make a class effectively private // with private constructors: class Soup { private Soup() {} // (1) Allow creation via static method: public static Soup makeSoup() { return new Soup(); } // (2) Create a static object and // return a reference upon request. // (The "Singleton" pattern): private static Soup ps1 = new Soup(); public static Soup access() { return ps1; } public void f() {} } class Sandwich { // Uses Lunch void f() { new Lunch(); } } // Only one public class allowed per file: public class Lunch { void test() { // Can't do this! Private constructor: //! Soup priv1 = new Soup(); Soup priv2 = Soup.makeSoup(); Sandwich f1 = new Sandwich(); Soup.access().f(); } } ///:~
④:實際上,Java 1.1內部類既可以是“受到保護的”,也可以是“私有的”,但那屬於特別情況。第7章會詳細解釋這個問題。
⑤:亦可通過從那個類繼承來實現。
迄今為止,我們創建過的大多數方法都是要麼返回void,要麼返回一個基本數據類型。所以對下述定義來說:
public static Soup access() {
return psl;
}
它最開始多少會使人有些迷惑。位於方法名(access)前的單詞指出方法到底返回什麼。在這之前,我們看到的都是void,它意味著“什麼也不返回”(void在英語裡是“虛無”的意思。但亦可返回指向一個對象的句柄,此時出現的就是這個情況。該方法返回一個句柄,它指向類Soup的一個對象。
Soup類向我們展示出如何通過將所有構建器都設為private,從而防止直接創建一個類。請記住,假若不明確地至少創建一個構建器,就會自動創建默認構建器(沒有自變量)。若自己編寫默認構建器,它就不會自動創建。把它變成private後,就沒人能為那個類創建一個對象。但別人怎樣使用這個類呢?上面的例子為我們揭示出了兩個選擇。第一個選擇,我們可創建一個static方法,再通過它創建一個新的Soup,然後返回指向它的一個句柄。如果想在返回之前對Soup進行一些額外的操作,或者想了解准備創建多少個Soup對象(可能是為了限制它們的個數),這種方案無疑是特別有用的。
第二個選擇是采用“設計方案”(Design Pattern)技術,本書後面會對此進行詳細介紹。通常方案叫作“獨子”,因為它僅允許創建一個對象。類Soup的對象被創建成Soup的一個static private成員,所以有一個而且只能有一個。除非通過public方法access(),否則根本無法訪問它。
正如早先指出的那樣,如果不針對類的訪問設置一個訪問指示符,那麼它會自動默認為“友好的”。這意味著那個類的對象可由包內的其他類創建,但不能由包外創建。請記住,對於相同目錄內的所有文件,如果沒有明確地進行package聲明,那麼它們都默認為那個目錄的默認包的一部分。然而,假若那個類一個static成員的屬性是public,那麼客戶程序員仍然能夠訪問那個static成員——即使它們不能創建屬於那個類的一個對象。