讀古今文學網 > Android程序設計:第2版 > 作用域 >

作用域

作用域決定了程序中的變量、方法和其他符號的可見範圍。任何符號在其作用域外都是完全不可見的,不能被使用。在這一節我們將簡要地介紹作用域的主要內容,從最高層開始。

Java包

Java包提供了一種機制,它把相關類型分組到一個全局唯一的命名空間。這種分組機制可以防止在一個包的命名空間內的標識符和其他開發人員在其他命名空間內創建和使用的標識符衝突。

一個典型的Java程序由很多Java包的代碼組成。典型的Java運行時環境提供了如java.lang和java.util這樣的包。此外,程序可能會依賴於其他通用的庫,類似於org.apache樹。傳統上,應用代碼(你所創建的代碼)在你所創建的包內,包名是通過反轉域名並附加程序名字生成的。因此,如果你的域名是androidhero.com,你的包所屬的樹的根是com.androidhero,則可以把代碼放到如com.androidhero.awesomeprogram和com.androidhero.geohottness.service這樣的包中。用於Android應用的典型的包在佈局上會包含一個持久性包、UI包和負責應用邏輯和控制器代碼的包。

包除了定義了全局唯一的命名空間之外,包內對象的成員(成員變量和方法)之間的可見性也不同。類的內部變量對於在同一個包內的類是可見的,而對於其他包內的類則是不可見的。這個話題在後面還會進一步探討。

聲明一個類屬於某個包的方法是,在定義類的文件的最上方,使用package這個關鍵字按照下面這個方式聲明:


package your.qualifieddomainname.functionalgrouping
  

不要過分簡化包名!因為一個快速、臨時的實現方式可能需要使用很多年,如果不能保證包名唯一,那麼以後一定會深受其困擾。

一些大型的項目通過使用完全不同的頂級域名來實現公有API包和這些API的實現之間的隔離。舉個例子,Android API使用頂級包名android,這些API的實現則在com.android包內。Sun的Java源代碼採用的機制也類似於此。公有API在Java包內,但是這些API的實現則放在了sun包內。在任意一種情況下,如果一個應用導入的是某個實現包,則這個應用會反覆無常,因為它依賴於一些非公有的API。

雖然把代碼添加到已有的包內是可以的,但通常認為這是一種不好的做法。通常情況下,除了名字空間,包通常是一棵源代碼樹,其至少和逆轉的域名一樣高。雖然這只是傳統習慣,但是Java開發人員通常會期望com.brashandroid.coolapp.ui這個包中包含了CoolApp UI的所有源代碼。如果另一棵樹的某些地方也有CoolApp UI的一些代碼,很多人會覺得很不習慣。

注意:Android應用框架中也有包的概念。但它不同於這裡所說的包,在第3章會對Android應用框架的這種包進行專門說明。不要把它和Java的包相混淆。

關於Java包的更多信息,請參閱Java教程:http://download.oracle.com/javase/tutorial/java/package/packages.html。

訪問修飾符和封裝

前文提過,類的成員有特殊的可見性規則。大多數Java塊中的定義是有作用域的:它們只在代碼塊本身及內嵌於其中的代碼塊中可見。然而,類中的定義在代碼塊外也可能是可見的。Java支持類將其頂級成員(其方法和成員變量)通過訪問修飾符(access modifiers)發佈給其他類的代碼。訪問修飾符關鍵字修改了聲明的可見性。

在Java中有3個訪問修飾符關鍵字:public、protected和private。共支持4種訪問級別。訪問修飾符影響的是類成員在類外面的訪問性,但類內部的代碼塊遵循的是普通的作用域,不需要考慮訪問修飾符的影響。

private修飾符的限制最高。帶private關鍵字的聲明在代碼塊外是不可見的。這種聲明是最安全的,它會確保僅在類的內部會有指向這個聲明的引用。private聲明越多,類就越安全。

限製程度僅次於private修飾符的是默認的訪問限制,即package訪問。沒有任何修飾符的聲明屬於默認情況,默認的訪問可見性是指只能在同一個包中的其他類中可見。默認訪問是創建對像共享的一種非常便捷的方式,Java的默認聲明和C++中的friend聲明類似。

protected訪問修飾符除了支持所有的默認訪問權限之外,還允許訪問子類。任何包含protected聲明的類都能夠訪問這些聲明。

public訪問修飾符是限制條件最弱的修飾符,其允許從任何地方對它進行訪問。

下面的這個例子對這些修飾符的使用方式進行了具體的演示。有4個類,分別屬於兩個不同的包over.here和over.there,這4個類都引用了其中類Accessible中聲明的成員變量:


package over.here;
public class Accessible {
    private String localAccess;
    String packageAccess;
    protected String subtypeAccess;
    public String allAccess;
    public void test {
        // all of the assignments below work:
        // the fields are declared in an enclosing
        // block and are therefore visible.
        localAccess = \"success!!\";
        packageAccess = \"success!!\";
        subtypeAccess = \"success!!\";
        allAccess = \"success!!\";
    }}
package over.here;
import over.here.Accessible;
// this class is in the same package as Accessible
public class AccessibleFriend {
    public void test {
        Accessible target = new Accessible;
        // private members are not visible
        // outside the declaring class
        target.localAccess = \"fail!!\"; // ERROR!!
        // default access visible within package
        target.packageAccess = \"success!!\";
        // protected access is superset of default
        target.subtypeAccess = \"success!!\";
        // visible everywhere
        target.allAccess = \"success!!\";
    }
}
package over.there;
import over.here.Accessible;
// a subtype of Accessible
// in a different package
public class AccessibleChild extends Accessible {
    // the visible fields from Accessible appear
    // as if declared in a surrounding block
    public void test {
        localAccess = \"fail!!\"; // ERROR!!
        packageAccess = \"fail!!\"; // ERROR!!    
        // protected declarations are
        // visible from subtypes
        subtypeAccess = \"success!!\"; 
        // visible everywhere
        allAccess = \"success!!\";
    }
}
package over.there;
import over.here.Accessible;
// a class completely unrelated to Accessible
public class AccessibleStranger {
    public void test {
        Accessible target = new Accessible;
        target.localAccess = \"fail!!\"; // ERROR!!
        target.packageAccess = \"fail!!\"; // ERROR!!
        target.subtypeAccess = \"success!!\"; // ERROR!! 
        // visible everywhere
        target.allAccess = \"success!!\";
    }
}