訪問控制(1)
- 訪問控制和代碼重構有關。
- 需要考慮一條設計原則:
將不變的東西和變的東西分開來 - 類庫使用者依賴類庫,在類庫發生變化的時候不需要對代碼進行更改。通過某些約定可以實現這個準則:
- 類庫編程人員必須保證不移除現有方法。
- 類庫編程人員遇到的問題:不知道哪些字段或方法被客戶端程序員所使用,導致對類庫的修改束手束腳。
- 問題的解決方案:Java訪問修飾符
- 訪問修飾符的訪問級別,由高到低:
- public
- protected
- package access
- private
- 作爲類庫設計人員,最終會盡可能將類的成員私有,只暴露希望客戶端程序員操作的方法。
- 僅擁有類庫組件和對類庫組件訪問控制的概念是不夠的,還需要知道類庫組件是如何組裝成在一個類庫單元中。這一切都由package關鍵詞進行把控。訪問修飾符的效果亦是受到類在同一個包還是單獨的包的影響。
訪問控制(2)
- 包裏面的的類都組織在同一個命名空間下。
- 使用類的方式:
- 類的全名。
- import。
- 爲了引入所有的類,使用*。
代碼示例:
import java.util.*;
- 導入提供了命名空間管理的機制。
- 每個.java文件都僅有一個和文件同名的public類。文件中的其他類都是支撐類。不可以在包以外進行訪問。
- java文件編譯後,會輸出class文件,java文件中每個類對應一個class文件。
- java將class文件壓縮打包成JAR文件。java解釋器負責查找、加載和解釋class文件。
- 爲了說明每個組件屬於一個整體,需要使用package。
- package表達式必然出現在java文件首個非註釋行。
代碼示例:
package access;
- 包名的使用規範是全小寫。
- package和import的用處是保證了類命名空間的唯一性。
- 由於java的包是由一系列class文件組成的,爲了避免文件過多造成的雜亂,java採用了系統的文件分層機制。
- 包名通過class文件的路徑名編碼得到。
- 約定包名的第一部分爲反向域名。第二個部分爲class文件所在的路徑名。
- java解釋器工作原理:
- 查找環境變量CLASSPATH
- 基於CLASSPATH的條目、包名和類名,查找類文件
- 如果同時include了兩個庫且庫中使用了相同的名稱的類。爲了避免潛在的衝突,需要使用全量類名。
- java沒有了條件編譯。但是通過import不同的包,可以實現不同條件下的編譯。
訪問控制(3)
- 包訪問:沒有訪問修飾符的類成員。表示可以被同一包內其他的類訪問。包以外的不可訪問。
- 包訪問將同一個包內相關類組織在一起,並相互作用。
- 允許對類成員進行訪問的方式:
- 用public修飾成員
- 使成員可以包訪問,並將訪問類放在同一包內
- 如果是繼承類,則可以訪問protected成員和public成員。但是不能訪問包訪問權限成員。
- 使用get/set方法
- public:接口訪問權限
- 默認包訪問:不指定具體包的文件,編譯後在同一包內,可以認爲是默認包訪問
- private訪問:類成員不能被除本類以外的其他類訪問。
- 任何認爲是類的“助手”方法都可以指定爲private。
- 除非必須暴露底層設計,否則所有成員變量都應該設置爲private。
- protected:繼承訪問
- 基類中有些成員,允許繼承類訪問,但不允許全局訪問,而且protected具有包訪問權限。同一包中的其他類可訪問。
訪問控制(4)
- 訪問控制和實現隱藏相關,將數據、方法包裝在類裏以及實現的隱藏被稱爲封裝。
- 使用訪問控制的理由:
- 設定客戶端程序員可用的和不可用的代碼。
- 將接口和實現分離
- 可以採用的類創建風格:將public成員放在開始,後面跟着protected成員,包訪問成員和private成員。
訪問控制(5)
- 訪問修飾符也可以用來決定庫中的哪個類可以被訪問。
- 訪問控制符出現在class關鍵詞之前
- 類訪問的限制:
- 一個文件中只能有一個public修飾的類。
- public修飾的類的名稱必須準確和文件名相匹配。
- 有可能文件中沒有public修飾的類。此時可以任意給文件命名
- 如果想使用包中的某個類,並且不希望對這個類的任意修改影響客戶端程序,那麼就移除類的public訪問權限。
- 包訪問權限的類成員字段還是儘可能設置爲private。但是方法可以設置爲與類訪問權限一致,如果強制要求public,可以設置爲public。
- 類不可以設置爲private或者protected(內部類除外)
- 如果不希望類可以被其他使用者訪問,可以將構造器設置爲private,然後在類內部的靜態方法中創建對象。
代碼示例:
package access;
/**
* @author vincient
* @create 2020-02-08 11:12 AM
*/
public class Lunch {
void testPrivate() {
// 不可用!
// Soup1 soup = new Soup1();
}
void testStatic() {
Soup1 soup = Soup1.makeSoup();
}
void testSingleton() {
Soup2.access().f();
}
}
class Soup1 {
private Soup1() {
}
public static Soup1 makeSoup() {
return new Soup1();
}
}
class Soup2 {
private Soup2() {
}
private static Soup2 ps1 = new Soup2();
public static Soup2 access() {
return ps1;
}
public void f() {
}
}