一、封裝
1、什么是封裝?
在一個對象中,包含了狀態(tài)和行為,狀態(tài)指的是對象的數(shù)據(jù)成員,而行為則對應(yīng)了對象的方法。數(shù)據(jù)成員和方法都是提供給內(nèi)部和外部使用的,內(nèi)部通過this調(diào)用對象的數(shù)據(jù)成員和方法,在外部另一個類中則通過 對象.數(shù)據(jù)成員 和 對象.方法()來調(diào)用對象的數(shù)據(jù)成員和方法。
將要封裝的對象成員(數(shù)據(jù)成員和方法),只允許從外部調(diào)用部分成員。
2、封裝的作用
封裝的作用是提高對象的易用性和安全性。
譬如,一個充電電筒:
一個用戶即使不看說明書,也可以猜到這個電筒的操作: 開關(guān)和充電。這個電筒用一個塑料殼將用戶不需要接觸的內(nèi)部細節(jié)隱藏起來,只保留了兩個接口,開關(guān)和電插頭。使用這兩個接口,用戶足以使用該產(chǎn)品在設(shè)計中想要實現(xiàn)的功能。如果所有的細節(jié)都同時暴露給用戶,那么用戶會對產(chǎn)品感到不知所措 (比如下面不加殼的遙控器)。因此,封裝提高了產(chǎn)品的易用性。
對于Java來說,若不對數(shù)據(jù)成員和方法進行封裝,面對茫茫多的數(shù)據(jù)成員時,我們在需要用到幾個數(shù)據(jù)成員的組合值時,這里的數(shù)據(jù)成員就相當于手電筒的內(nèi)部部件,數(shù)據(jù)成員的組合值,就相當于手電筒的內(nèi)部部件組合成的開關(guān)功能,當我們需要這個組合值的時候,直接調(diào)用就可以了,而不是還要我們在外部一個一個成員數(shù)值組起來再構(gòu)成那個組合值的功能。你看,要是我們封裝起來了,是不是更容易使用了?
對于安全性來說,這里我們用遙控器打比方,如果產(chǎn)品不封裝,遙控器的許多細節(jié)會暴露在用戶面前: 電池、電路、密封的橡膠等等。盡管這可以讓用戶更自由的對產(chǎn)品實施操作,比如直接給電池放電,取出一個LED燈等等。然而,用戶往往要承擔更大的損壞產(chǎn)品的風(fēng)險。因此,封裝提高了產(chǎn)品的安全性。
同樣,對于Java,譬如有3個參數(shù),int a,int b,int c,而a=b/c,若我們沒有對其封裝,沒有對c進行判斷不能為0,當我們運行程序時,輸入c=0時,就會產(chǎn)生異常。當一個類的數(shù)據(jù)成員直接暴露給外部使用,在外部使用人員不了解或者惡意破壞的情況下,就有可能產(chǎn)生不可預(yù)想的后果,這里也就體現(xiàn)了封裝的安全性。
對象成員的封裝
Java通過三個關(guān)鍵字來控制對象的成員的外部可見性(visibility):public,private,protected。
這里我們主要講public 和 private 這兩個關(guān)鍵字。
public: 該成員外部可見,即該成員為接口的一部分
private: 該成員外部不可見,只能用于內(nèi)部使用,無法從外部訪問。
我們先來封裝以前定義的Human類:
public class Test
{
public static void main(String[] args)
{
Human aPerson = new Human(160);
System.out.println(aPerson.getHeight());
aPerson.growHeight(170);
System.out.println(aPerson.getHeight());
aPerson.repeatBreath(100);
}
}
class Human
{
/**
* constructor
*/
public Human(int h)
{
this.height = h;
System.out.println("I'm born");
}
/**
* accessor
*/
public int getHeight()
{
return this.height;
}
/**
* mutator
*/
public void growHeight(int h)
{
this.height = this.height + h;
}
/**
* encapsulated, for internal use
*/
private void breath()
{
System.out.println("hu...hu...");
}
/**
* call breath()
*/
public void repeatBreath(int rep)
{
int i;
for(i = 0; i < rep; i++) {
this.breath();
}
}
private int height; // encapsulated, for internal use
}
內(nèi)部方法并不受封裝的影響。Human的內(nèi)部方法可以調(diào)用任意成員,即使是設(shè)置為private的height和breath()
外部方法只能調(diào)用public成員。當我們在Human外部時,比如Test中,我們只能調(diào)用Human中規(guī)定為public的成員,而不能調(diào)用規(guī)定為private的成員。
通過封裝,Human類就只保留了下面幾個方法作為接口:
getHeight()
growHeight()
repBreath()
我們可以將Human類及其接口表示為如下圖的形式:
如果我們從main中強行調(diào)用height:
System.out.println(aPerson.height);
將會有如下錯誤提示:
Test.java:6: height has private access in Human
System.out.println(aPerson.height);
^
1 error
Beep, 你觸電了! 一個被說明為private的成員,不能被外部調(diào)用。
在Java的通常規(guī)范中,表達狀態(tài)的數(shù)據(jù)成員(比如height)要設(shè)置成private。對數(shù)據(jù)成員的修改要通過接口提供的方法進行(比如getHeight()和growHeight())。這個規(guī)范起到了保護數(shù)據(jù)的作用。用戶不能直接修改數(shù)據(jù),必須通過相應(yīng)的方法才能讀取和寫入數(shù)據(jù)。類的設(shè)計者可以在接口方法中加入數(shù)據(jù)的使用規(guī)范。