官术网_书友最值得收藏!

4.2 類封裝與訪問控制

封裝是面向對象程序設計技術的三大基本特征之一。通過封裝,對象對外部隱藏了它的狀態和行為的具體實現,使對象得以保持獨立性。Java語言提供了多個關鍵字對域變量和方法的訪問范圍進行限制,以便實現不同程度的封裝。

4.2.1 類封裝

類是對現實世界實體的抽象,將抽象出來的狀態和行為結合在一個封裝的整體里,這個封裝體就是類。類的屬性在代碼中以域變量的方式表示,類的對象各個屬性的當前值就是對象的狀態;外部可見的對象活動就是對象的行為,類中定義的方法是針對其對象實施的操作或服務,也就是說,方法用來實現對象的行為(參見圖4.8)。對象的狀態通過其行為改變,也就是屬性值通過調用方法來改變。

圖4.8 對象的狀態和行為抽象為類的屬性和方法

類的設計應該將對象的狀態和行為的真實內部實現細節隱藏起來,禁止外部直接訪問它的狀態信息,而在類的內部定義對其對象的狀態實施訪問、操作和管理的方法,對外界提供和保持一個定義良好的接口。

開發中的一個Java程序是由若干個類組成的。面向對象程序設計方法要求類的設計應該做到最大化封裝和最小化耦合。最大化封裝是指每個類的設計越獨立越好,每個類不應該對它的任何內部屬性提供對象之間的直接訪問,例如一個對象不能直接修改另外一個對象的屬性值;類應該向外界提供能實現其職責的最少數目的方法,向外界提供的接口應該盡量少受類內部設計變化的影響。最小化耦合是指一個類應該盡可能地通過自身定義的方法實施對其對象的操作和管理,只依賴于其他類的公有接口而不依賴于其他類內部實現與其交互;如果類之間要實現共同職責而必須耦合,也必須把這種耦合對外界的影響降到最低。

運行中的Java程序是由一些互操作的對象構成的。一個對象發送消息給其他對象實施消息傳遞或動作請求,從而使對象之間進行互操作,在Java程序中對象通過調用其他對象的方法互操作得到處理。

4.2.2 訪問控制

Java程序設計對類中定義的屬性和方法的使用范圍需要進行限制,甚至也需要對類的使用范圍進行限制,這種限制是通過使用Java語言的訪問修飾符實現的。訪問修飾符也是實現類的封裝性的工具。

1. public修飾符

使用public修飾符的屬性和方法既可以在定義它們的類中使用,也可以在任何其他類中使用。一般地,需要提供給任意其他類使用的公共方法都應該采用public修飾符進行限定。例如,例4.3設計的User類中自動生成的構造方法、getter和setter方法、hashCode和equals方法都使用public修飾(見程序清單4.1),它們都是使用User類時的接口方法。類的對象大多數情況下需要在別的類中創建,因此類的構造方法基本都采用public修飾。在UML類圖中public方法前面以+標記。

一些屬性也需要提供給外界任意類使用,例如,Integer類中的MIN_VALUE、MAX_VALUE等屬性。在UML類圖中public字段前面以+標記。

當修飾符public用于修飾一個類的定義時,指明這個類可以在任何類中使用。應注意,Java 9新引入的模塊化系統對public類的訪問性進行控制,即類是存在于模塊中的,模塊必須明確聲明其中哪些公共類可以被其他模塊訪問。除非模塊明確地使其公共類可訪問,否則其他模塊就不能訪問另一個模塊中的公共類。

一個Java源程序文件是一個編譯單元,只能有一個public類,但可以有多個沒有public修飾的類,文件名必須與public類的類名完全相同,甚至文件名字母的大小寫也必須與類名保持一致,文件擴展名是.java。一個Java源程序文件中也可以沒有public類,此時文件基本可以隨意取名,擴展名仍然是.java。

2. 包訪問性

包(Package)既是解決類、變量和方法名字沖突的機制,也是訪問控制的工具。

許多情況下,Java程序會在Internet中運行,類在程序運行時自動下載,因此需要防范兩個封裝了不同類型對象的類使用相同的名字從而發生沖突的情況,辦法是將類定義在包中。包采用域名倒序方法取名,而域名在Internet上是唯一的,這樣就可以保證帶有包名的類名也是唯一的。

同一個包中的類具有訪問的友好性,即如果類名、屬性名和方法名前面不加任何訪問修飾符就是默認訪問控制,此時當前包內的其他所有類都能訪問它們,但包外的所有類都不能訪問。利用這種機制,將相關的類都組合到一個包里,可以使它們相互之間方便地進行溝通。

例如,例4.2中的兩個類ObjSwap和TwoInt都位于同一個包book.methoddemos中,因此在ObjSwap類中可以直接訪問TwoInt類的對象var的域變量var1和var2。

3. private修飾符

使用private修飾的屬性和方法只能在其所在的類中使用,任何其他類都不能直接訪問它們。因此,private修飾符是實現類封裝和代碼隱藏的主要工具。例如,例4.3中盡管步驟(2)添加字段name、password和job時沒有使用任何訪問修飾符,但是在步驟(4)“生成getter和setter”對話框中選擇了“封裝字段”復選框,因此NetBeans IDE自動為這三個屬性添加了private修飾符以封裝它們。

如果類的屬性需要在該類之外訪問,通常還是將該屬性定義為private訪問性以符合封裝的原則,同時為該屬性提供具有public訪問性的取值方法(也稱訪問器或getter)和設值方法(也稱修改器或setter)。取值方法名以get開頭,設值方法以set開頭,后面跟屬性名,且屬性名的第一個字母大寫。取值方法返回值與屬性類型相同;設值方法無返回值,但需要定義與屬性類型相同的參數。例如,例4.3中步驟(4)為User類的字段name、password和job自動生成的取值方法和設值方法就是按照這個慣例編碼的。

在UML類圖中private字段和方法前面以-標記。

4. protected修飾符

可以以現有的類為父類定義一個新類,這個新類是該現有類的子類,詳細內容4.3節介紹。

一個類中使用protected修飾符的屬性和方法既可以被同一個包中的其他類直接訪問,也可以被不同包中該類的子類直接訪問。

Java語言的訪問修飾符及其對類成員訪問控制的總結如表4.1所示。

表4.1 訪問控制表

4.2.3 static修飾符

一般情況下,類中定義的域變量和方法是屬于具體對象的,例如,User類中的字段name、password和job是一個具體用戶的姓名、密碼和身份,描述所有用戶的User類不可能有一個具體的姓名、密碼和身份。因此,對于類中定義的域變量和方法總是需要通過具體對象訪問和調用。如果在User類中設置一個字段id用來對用戶編號,且每個用戶都有一個唯一編號,那么這個id字段是所有用戶公用的,不能被任何一個用戶控制,而是需要被User類統一控制。類的這種對于所有對象公用的字段,Java語言使用static修飾符定義,說明它是類變量,屬于整個類。類的對象也稱為這個類的一個實例,相應地將屬于具體對象的域變量稱為實例變量。

對于static域變量的操作應該在屬于整個類的方法中進行,這種方法也使用static進行修飾。不能在非static方法中直接訪問static域變量。

static域變量和方法稱為靜態成員,對它們的訪問使用類名進行,即采用以下格式訪問:

     類名.靜態字段名
     類名.靜態方法名(實參表)

這種用法在前面的例題程序中十分普遍。例如,例3.1的IfMaxDemo類中第13行語句“int maxNum=Integer.MIN_VALUE;”引用的就是Integer類中的靜態域MIN_VALUE(此處是常量),多個例題程序調用Integer類的靜態方法parseInt(Integer.parseInt(text))。顯然,不用創建對象就可以使用類中的域變量和方法,程序顯得更為簡潔。

自Java 5開始,還增加了一個靜態導入語句,即在import關鍵字之后緊接著一個static關鍵字,在導入的類名之后添加一個句點和一個星號或一個靜態方法名。即靜態導入語句的格式是:

     import static 包名.類名.*;

     import static 包名.類名.靜態方法名;

然后就可以直接用方法名調用靜態方法,而不必前面綴有類名。這種用法在一個靜態方法在程序中大量使用時可以減少輸入,但是要小心避免方法名的沖突。

例4.4 例4.3設計的用戶封裝類User添加用戶編號字段id,使每個用戶都有一個唯一編號。

解:按照以下步驟操作。

(1)在User類中添加字段id。方法是在User類中添加語句“private static int id;”。

(2)為字段id添加訪問器和修改器。方法與例4.3步驟(4)相同。完成操作后添加的源代碼如下。

(3)在構造方法中添加語句“id++;”,使程序在每創建一個User對象時編號增加,避免不同用戶的編號發生重復。

主站蜘蛛池模板: 玉山县| 敦化市| 台南市| 民乐县| 莆田市| 临高县| 孟村| 井陉县| 芜湖县| 安龙县| 寻甸| 和田市| 晋宁县| 汪清县| 卢湾区| 茂名市| 红安县| 深圳市| 监利县| 漳州市| 武清区| 河东区| 重庆市| 上犹县| 黄大仙区| 奉新县| 溆浦县| 孟村| 平邑县| 玛纳斯县| 惠水县| 宁海县| 玉屏| 湖口县| 余庆县| 嫩江县| 天等县| 江城| 齐齐哈尔市| 德阳市| 景宁|