《Java編程思想》Chapter 6 : 訪問權限控制
- 1. 前言
- 1.1. 訪問權限控制的等級
- 1.2.
package
關鍵字的引入
- 2. 包:庫單元
- 2.1. 代碼組織
- 2.2. 包名的創建
- 3. Java訪問權限修飾詞
- 3.1. 包訪問權限
- 3.2.
public
: 接口訪問權限 - 3.3.
private
: 你無法訪問 - 3.4.
protected
: 繼承訪問權限
- 4. 接口和實現
- 4.1. 封裝
- 5. 類的訪問權限
- 5.1. 部分額外限制
- 5.2. 作用
- 5.3. 一些特殊用法
1. 前言
我們總是會發現我們希望對于我們的代碼進行重寫以改進
使得其更可讀、更易理解,因而更具有可維護性
這便是重構
但是我們改變代碼,就可能會引發它的變化
但對于使用者來說,會希望代碼保持不變
這邊產生一個問題:如何將代碼中變動的事物與不變的事物區分開來?
這對于類庫尤其重要
這便是訪問權限控制的作用
Java提供 訪問權限修飾詞
用于向代碼使用者指明,哪部分是可以用的,哪部分是不可以用的
1.1. 訪問權限控制的等級
從最大權限到最小權限
- public
任何地方都可以訪問 - protected
同一個包內,或者子類可以訪問 - 包訪問權限(no keyword, default)
同一個包內可以訪問 - private
只有類內可以訪問
1.2. package
關鍵字的引入
對于 訪問權限修飾詞
還存在著如何將構件捆綁到一個內聚的類庫單元中的問題
Java使用 package
關鍵字來解決這個問題
2. 包:庫單元
**包(package)**內包含有一組類
被組織在單一的名字空間之下
也就是包名
我們一直使用的導入,就是提供一個管理名字空間的機制
我們平時編寫的Java源文件,通常被稱為編譯單元(或轉譯單元)
每個編譯單元只能由不超過一個public
類
編譯單元中的public
類對包外可見
2.1. 代碼組織
我們可以發現,我們對一個編譯單元進行編譯后
其中的每個類都會有一個輸出文件,有著.class
的后綴
他們可以被打包并壓縮為一個java文檔文件(JAR,使用Java的jar文檔生成器)
而類庫則實際上是一組類文件
其中每個文件都有一個public
類,以及任意數量的非public
類
public
類的名字必須與文件名相同
關鍵字package
用于將類庫中的類組織在一起:
// 在一個包中的類
package PackageTest;
若使用package
語句,則其必須是文件中除注釋外的第一句代碼
包的命名規則:
package
的命名全部使用小寫字母,包括中間的字
對于package
與import
的理解:
將單一的全局名字空間分割
使得不論多少人進行編寫,也不會出現名稱沖突的問題
2.2. 包名的創建
包從未整被真正打包為單一的文件
且一個包可以有多個.class
文件構成
這可能造成復雜的情況
為了避免這種情況,我們將.class
文件都置于一個目錄之下
利用操作系統的層次化化文件結構來解決此問題
這是Java解決混亂問題采用的一種方式
同時另外兩個問題也可以被解決:
- 如何創建獨一無二的名稱?
- 如何查找可能隱藏于目錄結構中某處的類?
包名的創建:
依據慣例,報名使用創建者的反順序域名
(域名是第一無二的,那么包名因而也是獨一無二的)
沒有域名,也可以采用不太可能重復的組合
例子top.thesumst.lab
尋址方法:
把package
名稱分解為系統上的一個目錄
然后在該目錄下尋找.class
文件
Java解釋器尋找.class
運行過程大致圖解:
graph LRA[Java解釋器] --> B[尋找CLASSPATH環境變量]B --> C[尋找包名]C --> |包名重構,將點替換為路徑|D[尋找包名對應的目錄]D --> E[尋找類文件]E --> F[加載類]
p.s. 使用jar文件時,必須在類路徑中將jar文件的實際名稱寫清楚
(實際上可以理解為一個目錄的根目錄)
沖突處理:
可以想象,還是喲可能出現幾個包中出現重復類名的情況
如果我們使用通配符*
導入包,那么可能會出現問題
此時我們需要指明具體的包名
或者我們可以使用import
語句的全名形式,指定導入類
從而大大降低了沖突的可能性
p.s. 默認包:當沒有使用package
語句時,類位于默認包中,同樣目錄下的默認包中的類之間具有包訪問權限
3. Java訪問權限修飾詞
Java中的四種Java訪問權限修飾詞:
public
protected
包訪問權限(no keyword, default)
private
他們用于在類中每個實例變量或方法前進行修飾
并僅作用于這個定義
其中,不提供任何訪問權限修飾詞,意味著包訪問權限
3.1. 包訪問權限
包訪問權限意味著對當前包中的所有其他類都可見
但是包之外的所有類,都沒有這個成員的訪問權限
因而希望取得某成員的訪問權,我們只有幾個途徑:
public
聲明- 不加訪問權限修飾詞,將其他類至于同一個包中
protected
聲明,繼承此類的子類可以訪問- 對于
private
聲明,提供訪問器(getter
)和修改器(setter
)方法
使得其他類可以通過這兩個方法訪問
OOP中最優雅的方式
3.2. public
: 接口訪問權限
public
關鍵字修飾成員,表示其對每個人都是可用的
3.3. private
: 你無法訪問
private
關鍵字修飾成員
表示除了成員所示類之外,其他任何類都無法訪問
使用場景舉例:
- 控制類的對象的創建
顯示定義構造器,并將其聲明為private
此時我們可以通過提供一個public
的靜態方法來創建對象
這有利于我們控制對象的創建
(還會組織對于此類的繼承) - "助手"方法
如果一個方法,我們確定它只會被用于輔助類中的別的方法
我們可以將它指定為private
可以組織我們在包內的其他地方對其進行誤用 - 域一般應該指定為
private
通過提供public
方法來訪問域
使得我們可以控制對域的訪問
除非需要公開底層的實現細節(不太常見),否則一般推薦將所有域都指定為private
3.4. protected
: 繼承訪問權限
protected
關鍵字修飾成員
主要用于處理集成概念
說明這個成員對于繼承的子類是可見的
此外,還提供包訪問權限
4. 接口和實現
4.1. 封裝
對訪問權限的控制,常被稱為具體實現的隱藏
將數據和方法包裝進類中,以及具體是實現的隱藏,常共同被稱為封裝
結果是得到的同時帶有特征和行為數據類型
訪問權限控制將權限控制于數據類型的內部
兩個重要原因:
- 要設定代碼中,可以被使用和不可以被使用的界限
一般我們稱使用者為客戶端程序員 - 接口和具體實現進行分離
如果我們限制客戶端程序員除了向接口發送信息之外不可以進行別的操作
那么我們可以在不破壞客戶端代碼的情況下
隨意修改任何不是public
的東西(也就是接口之外的東西)
常用的實踐模式:
將public
成員至于開頭
隨后耕者protected
、包訪問權限、private
成員
這樣便于類的使用者抓住重點
因為這樣他們只需要閱讀public
部分,也就是他們需要同時也是可以訪問的部分
5. 類的訪問權限
不同于類的成員
類的訪問權限只有兩種:public
和包訪問權限
5.1. 部分額外限制
- 每個編譯單元只能有一個
public
類 public
類的必須完全與其所處的編譯單元的文件名相同
包括大小寫- 編譯單元內可以不帶
public
類(不常用)
5.2. 作用
確保客戶端程序員只使用我們希望提供給外部使用的類
而非我們可能知識用于內部實現,或者后續很可能更改或刪除的類
5.3. 一些特殊用法
如果我們不希望其他任何人擁有某個類的訪問權限
我們可以將其所有的構造器都指定為private
如此,除了該類static
成員內可以創建
其他任何人都無法創建該類的對象
這種操作有幾種可能的用途:
- 返回引用之前對對象做一些額外的工作
- 記錄對象的創建次數,可以限制對象的數量