Java 入門指南

單元 9 - 封裝與建構子

~~學習進度表~~

物件導向程式設計 (object-oriented programming) 有三大基本特性,分別是封裝 (encapsulation) 、繼承 (inheritance) 及多型 (polymorphism)

Encapsulation

Inheritance

Polymorphism

繼承的目的是讓類別 (class) 具有像是親屬的垂直關係(父母子女),子類別 (subclass) 可以擁有父類別 (superclass) 的屬性 (field) 及方法 (method) ,多型像是親屬的平行關係(兄弟姊妹),多個子類別繼承自單一父類別之時,這些子類別就可以用父類別代替,父類別如同家族裡的「姓」,子類別則是「名」。

繼承的英文原文 inherit ,中文意思泛指從什麼得到什麼,生物學上的遺傳也是用這個詞。

至於封裝的意思就是把屬性封在類別中,這還牽涉到程式設計中另一個重要的概念             資訊隱藏 (information hiding) ,主要就是不讓外界隨意存取類別的屬性,也就是說,只讓類別的屬性給同個類別的方法存取。

這就需要用到 private 修飾詞 (modifier) 了,當屬性宣告為 private 的時候,該屬性就只能由同一個類別存取,我們將 ClassDemo01 類別的屬性 a 封裝,改寫成 ClassDemo03 類別,程式如下

/*
 * 檔名:ClassDemo03.java
 * 作者:張凱慶
 * 網站:http://kaiching.org
 */
package classdemo03;

public class ClassDemo03 {
    private int a;
    
    // setter 方法
    public void setA(int p1) {
        a = p1;
    }
    
    // getter 方法
    public int getA() {
        return a;
    }
    
    public int doSomething(int p1) {
        return a + p1;
    }
    
    public static void main(String[] args) {
        ClassDemo03 d = new ClassDemo03();
        
        // 利用 setter 設定屬性
        d.setA(11);
        
        System.out.println();
        // 利用 getter 印出屬性 a
        System.out.println(d.getA());
        System.out.println(d.doSomething(1));
        System.out.println();
    }
}

首先,屬性宣告為 private ,這樣屬性就只限類別 中可存取

private int a;

權限修飾詞有三個,分別是 publicprivateprotected ,加上不宣告權限修飾詞的形式,共有四種不同的權限。

接著如果要讓外界可以存取已封裝的屬性,就要另外設置 publicgettersetter 方法

// setter 方法
public void setA(int p1) {
    a = p1;
}
    
// getter 方法
public int getA() {
    return a;
}

執行結果如下

11
12

留意設定屬性 a 已經改成在建立物件後呼叫 setter 方法

// 利用 setter 設定屬性
d.setA(11);

這是 setter 方法的用途沒錯,不過如果我們想要建立物件的同時設定好屬性,就需要自訂建構子 (constructor) ,所謂建構子是一種特別的方法 (method) ,特別的地方是在建構子是建立物件 (object) 時所執行的方法。

記不記得建立 ClassDemo01 時,需要用 new 關鍵字 (keyword) , new 後面接的是附上小括弧的類別 (class) 名稱

ClassDemo01 d = new ClassDemo01();

完整的 ClassDemo01.java ,請參考單元 8

類別名稱加上小括弧,其實就是呼叫建構子來建立物件,如果沒有自行定義建構子,編譯器會自動替我們補上一個。下面我們給 ClassDemo01 增加建構子當作例子,新類別寫在 ClassDemo04 中,如下

/*
 * 檔名:ClassDemo04.java
 * 作者:張凱慶
 * 網站:http://kaiching.org
 */
package classdemo04;

public class ClassDemo04 {
    private int a;
    
    // 這裡定義建構子
    public ClassDemo04(int p1) {
        setA(p1);
    }
    
    public void setA(int p1) {
        a = p1;
    }
    
    public int getA() {
        return a;
    }
    
    public int doSomething(int p1) {
        return a + p1;
    }
    
    public static void main(String[] args) {
        ClassDemo04 d = new ClassDemo04(12);
        
        System.out.println();
        System.out.println(d.getA());
        System.out.println(d.doSomething(1));
        System.out.println();
    }
}

建構子的定義如下

// 這裡定義建構子
public ClassDemo04(int p1) {
    setA(p1);
}

此例 ClassDemo04() 有一個 int 參數 (parameter) p1 ,然後直接以 p1 設定屬性 (field) a 。注意建構子沒有回傳值 (return value) ,這是因為建構子就是用來建立物件,因此預設回傳物件本身。

由於建構子有參數,因此呼叫建構子建立物件時也需要提供參數

ClassDemo04 d = new ClassDemo04(10);

建構子也可以多載 (overload) ,就是可以自行定義各種參數版本的建構子,其中包括沒有參數的版本,編譯器 (compiler) 預設的建構子也是沒有沒有參數的版本。

執行結果如下

12
13

下面我們介紹個小技巧,建構子或方法的參數識別字 (identifier) 也可以跟屬性一樣,這時候存取屬性就要利用 this 關鍵字了,我們將 ClassDemo04 改成用 this 存取屬性,如下

/*
 * 檔名:ClassDemo05.java
 * 作者:張凱慶
 * 網站:http://kaiching.org
 */
package classdemo05;

public class ClassDemo05 {
    private int a;
    
    public ClassDemo05(int a) {
        setA(a);
    }
    
    public void setA(int a) {
        // 利用 this 存取屬性
        this.a = a;
    }
    
    public int getA() {
        return a;
    }
    
    public int doSomething(int a) {
        // 參數跟屬性使用相同的識別字
        return this.a + a;
    }
    
    public static void main(String[] args) {
        ClassDemo05 d = new ClassDemo05(13);
        
        System.out.println();
        System.out.println(d.getA());
        System.out.println(d.doSomething(9));
        System.out.println();
    }
}

setter 部分改成用參數 a 設定屬性 a ,屬性前要用 this 加小數點存取

public void setA(int a) {
    // 利用 this 存取屬性
    this.a = a;
}

doSomething() 的地方也是

public int doSomething(int a) {
    // 參數跟屬性使用相同的識別字
    return this.a + a;
}

執行結果如下

13
22

基本的類別定義包含屬性、方法及建構子,接下來我們繼續討論物件導向程式設計的其他兩項特性。

相關教學影片

上一頁 單元 8 - 類別
回 Java 入門指南首頁
下一頁 單元 10 - 物件導向程式設計
回 Java 教材首頁
回程式語言教材首頁
中英文術語對照
class類別
compiler編譯器
constructor建構子
encapsulation封裝
field屬性
information hiding資訊隱藏
identifier識別字
inheritance繼承
keyword關鍵字
method方法
modifier修飾詞
object物件
object-oriented programming物件導向程式設計
overload多載
parameter參數
polymorphism多型
return value回傳值
subclass子類別
superclass父類別
參考資料
1. The Java™ Tutorials: Providing Constructors for Your Classes
2. The Java™ Tutorials: More on Classes
3. The Java™ Tutorials: Returning a Value from a Method
4. The Java™ Tutorials: Using the this Keyword
5. The Java™ Tutorials: Controlling Access to Members of a Class
6. The Java™ Tutorials: Understanding Class Members
7. The Java™ Tutorials: Initializing Fields
重點整理
1. 物件導向程式設計的三大特性為封裝、繼承與多型。
2. 封裝是限制成員只能在類別中存取,也就是把成員宣告為 private
3. 建構子是建立物件時所執行的特別方法。
4. 關鍵字 this 用在方法中存取屬性。
問題與討論
1. 物件導向程式設計有哪些特性?
2. 封裝的作用是什麼? Java 中要怎麼進行封裝?
3. 建構子是什麼?為什麼要建構子?
練習
1. 1. 建立一個新專案 Exercise0901 ,替 Exercise0805 類別設計建構子,將屬性 result 的初值放在建構子中設定。
2. 建立一個新專案 Exercise0902 ,替 Exercise0806 類別設計建構子。
3. 建立一個新專案 Exercise0903 ,替 Exercise0807 類別設計建構子。