Java 入門指南 - getter 與 setter

屬性 (field) 需要有效的封裝 (encapsulation) 到物件 (object) 裡頭,因此類別 (class) 定義屬性時,應該宣告 (declare) 為 private ,也就是私有的,只限定義他的類別可以存取,然後另外定義宣告為 public 的 getter 與 setter 方法 (method) ,利用 setter 設定屬性,另外用 getter 取得屬性值




為什麼要這麼做呢?理由其實很簡單,為了防止物件遭不當的使用,例如
Encrypt t = new Encrypt(); 
t.cArray = {'a'}; // 重新將 cArray 設定為另一個陣列


Encrypt 型態的變數 t 先以沒有參數版本的建構子 (constructor) 建立新物件,然後突然將 cArray 重新設定為對另一個陣列的參考,這樣一來,原先的對換表格就不見了說,往後加入編碼與解碼的方法,使用錯誤的表格,就會造成難以回復的結果。雖然這是我們刻意弄的例子,自己寫程式真實情況常常會有打錯字之類的,例如原本要將 a 設定為 1 ,結果按成 0
t.a = 0; // 把 a 設定錯誤


當然,我們自己開發程式可以自己小心點就好,但是,假如我們的類別準備提供給別的程式設計師或開發團隊使用的話,別人就不見得會這麼小心了。因此,物件導向程式設計 (object-oriented programming) 中提供封裝的意義即在此,類別的屬性應該宣告為 private
private char[] cArray = new char[26];
private int a;
private int b;
private int n;    
private char c;  
private int diff;


六個屬性就需要六個宣告為 public 的 getter 方法
public int getA() {
    return a;
}

public int getB() {
    return b;
}
    
public int getN() {
    return n;
}
    
public char getC() {
    return c;
}
    
public int getDif() {
    return diff;
}

public char[] getArray() {
    return cArray;
}


注意,建構子不需要回傳值型態,方法定義時需要標明回傳值型態,如果沒有回傳值,就須標上 void ,如另外六個 setter 方法
public void setA(int arg) {
    a = arg;
}
    
public void setB(int arg) {
    b = arg;
}
    
public void setN(int arg) {
    n = arg;
}
    
public void setC(char arg) {
    c = arg;
}
    
public void setDif(int arg) {
    dif = arg;
}

public void setArray() {
    int i, d, ci, m;
    char r;
    for (i = 0; i < n; i++) {
        ci = getC();
        d = ci * getA() + getB();
        m = d % getN();
        r = (char) (m + getDif());
        cArray[i] = r;
        c++;
    }        
}


我們在 setter 中直接設定屬性值,由於參數名稱全都採用 arg ,因此這裡可以用屬性名稱,不需要用到 this 。其中包含我們之前就介紹過 setArray() ,它也是 setter 方法之一,原先直接使用屬性名稱,這裡全部改成 getter 。


有關檢查 a 是否為奇數的部份,也可以寫進 setter 之中,但是我們把這部份寫在建構子裡,當然,這兩種方式都是可行的,讀者可自行選擇想要用哪一種方式。


因此原本建構子所用的 this 就改成呼叫 setter 囉!完整的 Encrypt 修改如下
public class Encrypt {
    private char[] cArray = new char[26];
    private int a;
    private int b;
    private int n;    
    private char c;  
    private int diff; 
    
    public Encrypt(int a, int b, int n, char c, int dif) {
        setA(a);
        setB(b);
        setN(n);
        setC(c);
        setDif(dif);
        setArray();
    }
    
    public Encrypt(int a, int b) {
        this(a, b, 26, 'a', 97);
    }
    
    public Encrypt() {
        int ta = 0;
        int tb = 0;
        
        while (ta % 2 == 0) {
            ta = (int) (Math.random() * 10);
            tb = (int) (Math.random() * 10);
        }
        
        setA(ta);
        setB(tb);
        setN(26);
        setC('a');
        setDif(97);
        setArray();
    }
    
    public void setA(int arg) {
        a = arg;
    }
    
    public int getA() {
        return a;
    }
    
    public void setB(int arg) {
        b = arg;
    }
    
    public int getB() {
        return b;
    }
    
    public void setN(int arg) {
        n = arg;
    }
    
    public int getN() {
        return n;
    }
    
    public void setC(char arg) {
        c = arg;
    }
    
    public char getC() {
        return c;
    }
    
    public void setDif(int arg) {
        diff = arg;
    }
    
    public int getDif() {
        return diff;
    }

    public void setArray() {
        int i, d, ci, m;
        char r;
        for (i = 0; i < n; i++) {
            ci = getC();
            d = ci * getA() + getB();
            m = d % getN();
            r = (char) (m + getDif());
            cArray[i] = r;
            c++;
        }        
    }
    
    public char[] getArray() {
        return cArray;
    }
}

/* 《程式語言教學誌》的範例程式
    http://pydoing.blogspot.com/
    檔名:Encrypt.java
    功能:示範 Java 程式 
    作者:張凱慶
    時間:西元 2011 年 4 月 */


由於 cArray 已經宣告為 private ,因此 EncryptDemo 中也要改成 getArray() 才能進行測試
// 測試 Encrypt 的類別
public class EncryptDemo {
    public static void main(String[] args) {
        System.out.println();
        for (int i = 0; i < 16; i++) {
            Encrypt t = new Encrypt();
            System.out.println(t.getArray());
        }
        System.out.println();
    }
}

/* 《程式語言教學誌》的範例程式
    http://pydoing.blogspot.com/
    檔名:EncryptDemo.java
    功能:示範 Java 程式 
    作者:張凱慶
    時間:西元 2011 年 4 月 */


請自行編譯測試,記得 Encrypt 與 EncryptDemo 都要重新編譯。接下來,我們替 Encrypt 加入編碼的功能吧!


中英文術語對照
屬性field
封裝encapsulation
物件object
類別class
宣告declare
方法method
建構子constructor
物件導向程式設計object-oriented programming


您可以繼續參考
軟體開發


相關目錄
回 Java 入門指南
回 Java 教材目錄
回首頁


參考資料
The JavaTM Tutorials: Getting Started
The JavaTM Tutorials: Learning the Java Language
The JavaTM Tutorials: Essential Classes
The Java Language Specification, Third Edition


本文於 2013 年 1 月訂正
本文於 2013 年 2 月修正

4 則留言:

aorta 提到...

您好,從頭學習java的新手,從你這邊受益良多
想跟您請教一下,在這邊將 Encrypt中加入了getter及setter之後,如果沒有對EncryptDemo做修改的話,

原本的
7 System.out.println(t.cArray);
會去存取到已經被設為private的cArray
所以在執行的時候就會得到錯誤訊息
cArray has private access in Encrypt

是不是應該也要將EncryptDemo中的段落修改成
7 System.out.println(t.getArray());

重新編譯之後,才能正確存取呢?
謝謝您的熱心指 導 :)

Kaiching Chang 提到...

這部份寫錯了,已修改,感謝指正 :)

symis 提到...

您好,#82. for (i = 0; i < n; i++) {
為何不是用getN()?
而RUN起來竟也沒有error!
謝謝指導!

Kaiching Chang 提到...

這裡寫成 getN() 會比較好唷!倒是 n 或 getN() 在 Encrypt 類別裡會是同義的, Encrypt 類別外才只能由 getN() 來存取 n 。