2016/4/28

OOP:封裝(Encapsulation) 以Java為例

來練習一下

本篇參考
封裝物件操作流程
封裝物件初始流程
封裝物件內部資料

我有一個Package叫Package1底下有Main.java和CashCard.java

CashCard.java


1
2
3
4
5
6


package Package1;
class CashCard {
    String number;
    int balance;
    int bonus;}




3 4 5行都是它的資料成員

Main.java


1
2
3
4
5
6
7
8
9


package Package1;
public class Main {
    public static void main(String[] args) {
        CashCard card1 = new CashCard();
        card1.number="A001";
        card1.balance=1000;
        card1.bonus=1;
    }//main}//class


透過new出來的參考名稱card1,可以存取物件,只要.運算子加上資料成員名稱


1
2
3
4
5
6
7
8
9
10
11
12
13
14


package Package1;
public class Main {
    public static void main(String[] args) {
        CashCard card1 = new CashCard();
        card1.number="A001";
        card1.balance=1000;
        card1.bonus=1;
        
        CashCard card2 = new CashCard();
        card2.number = "A002";
        card2.balance = 300;
        card2.bonus = 0;
    }//main}//class


但每次我要存取時都要做同樣動作,所以我們可以用建構子Constructor


1
2
3
4
5
6
7
8
9
10
11


package Package1;
class CashCard {
    String number;
    int balance;
    int bonus;
    CashCard(String number,int balance,int bonus){
        this.number = number;
        this.balance = balance;
        this.bonus = bonus;
    }}


Constructor是與class同名的method,不用宣告回傳型態

為了區別,在資料成員物件前+this關鍵字,表示將number、balance、bonus參數的值,指定給this那3個資料成員物件

以後使用者只需這樣寫,就可存取物件


1
2
3
4
5
6
7
8


package Package1;
public class Main {
    public static void main(String[] args) {
        CashCard card1 = new CashCard("A001",1000,0);
        CashCard card2 = new CashCard("A002",500,0);
        CashCard card3 = new CashCard("A003",300,1);
    }//main}//class


也可以這樣寫,下面是增強式for迴圈,結果與原始for一樣,增強式的card是自定義變數


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


package Package1;
public class Main {
    public static void main(String[] args) {
        CashCard cards[] = {
            new CashCard("A001",1000,0),
            new CashCard("A002",500,0),
            new CashCard("A003",300,1)
        };
        for(int i = 0 ; i<cards.length ; i++) {
            System.out.printf("%s %d %d %n",
                    cards[i].number,cards[i].balance,cards[i].bonus);
        }
        /*for(CashCard card : cards) {
            System.out.printf("%s, %d, %d %n",
                    card.number, card.balance, card.bonus);
        }*/
    }//main}//class


run:
A001 1000 0
A002 500 0
A003 300 1


再來就是定義method給使用者用,通常會寫在class


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41


package Package1;
class CashCard {
    String number;
    int balance;
    int bonus;
    CashCard(String number,int balance,int bonus){
        this.number = number;
        this.balance = balance;
        this.bonus = bonus;
    }
    void store(int money) {  // 儲值時呼叫的方法
        if(money > 0) {
            this.balance += money;
            if(money >= 1000) { 
                this.bonus++;
            }
        }
        else {
            System.out.println("儲值是負的?你是來亂的嗎?");
        }
    }
    void charge(int money) { // 扣款時呼叫的方法
        if(money > 0) {
            if(money <= this.balance) {
                this.balance -= money;
            }
            else {
                System.out.println("錢不夠啦!");
            }
        }
        else {
            System.out.println("扣負數?這不是叫我儲值嗎?");
        }
    }
    int exchange(int bonus) {  // 兌換紅利點數時呼叫的方法
        if(bonus > 0) {
            this.bonus -= bonus;
        }
        return this.bonus;
    }}


這樣就把儲值等等的method封裝起來了

這裡重點一下,如果函式不用回傳的話用void,要回傳要加回傳型態的關鍵字,還有return

以後使用者只要這樣寫:


1
2
3
4
5
6
7
8
9


        Scanner sc = new Scanner(System.in);
        CashCard card1 = new CashCard("B001",700,0);
        card1.store(sc.nextInt());
        
        CashCard card2 = new CashCard("B002",1700,0);
        card1.store(sc.nextInt());
        
        CashCard card3 = new CashCard("B003",2700,1);
        card1.store(sc.nextInt());


直接把要儲值的金額,呼叫函數,就可以了,也是我們常常用的

還有一點在Java慣例中,method名稱首字要小寫


1
2
3
4
5
6
7
8
9
10
11


        CashCard cards[] = {
            new CashCard("B001",700,0),
            new CashCard("B002",1700,0),
            new CashCard("B003",2700,1)
        };
        Scanner sc = new Scanner(System.in);
        for(int i = 0 ; i<cards.length ; i++){
            System.out.printf("為 (%s, %d, %d) 儲值:", cards[i].number,cards[i].balance, cards[i].bonus);
            cards[i].store(sc.nextInt());
            System.out.printf("明細 (%s, %d, %d)%n", cards[i].number,cards[i].balance, cards[i].bonus);
        }


現在可以這樣操作,結果:

run:
為 (B001, 700, 0) 儲值:1200
明細 (B001, 1900, 1)
為 (B002, 1700, 0) 儲值:3000
明細 (B002, 4700, 1)
為 (B003, 2700, 1) 儲值:510
明細 (B003, 3210, 1)



接下來,要為程式碼限制使用者能直接存取資料的漏洞

以目前程式來說,你希望通常使用者會這樣用:


1
2
3


Scanner sc = new Scanner(System.in);
CashCard card1 = new CashCard("A001", 500, 0);
card1.store(sc.nextInt());


但他們卻可以直接這樣用:


1
2
3


CashCard card1 = new CashCard("A001", 500, 0);
card1.balance += scanner.nextInt();
card1.bonus += 100;


直接跳過你寫的method,新增資料,這樣好嗎?這樣並不好,所以這時要用private關鍵字修飾一下,我們不要讓使用者直接存取私有資料

像這樣:


1
2
3
4
5
6
7
8
9


class CashCard {
    private String number;
    private int balance;
    private int bonus;
    CashCard(String number,int balance,int bonus){
        this.number = number;
        this.balance = balance;
        this.bonus = bonus;
    }
................


這時使用者balance的地方會編譯錯誤,那他System.out.printf("%d",card1.balance);  怎麼辦? 它看不到資料了啊

所以,要給使用者method讓他存取

像這樣:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28


...................................................
void charge(int money) { // 扣款時呼叫的方法
        if(money > 0) {
            if(money <= this.balance) {
                this.balance -= money;
            }
            else {
                System.out.println("錢不夠啦!");
            }
        }
        else {
            System.out.println("扣負數?這不是叫我儲值嗎?");
        }
    }
    int exchange(int bonus) {  // 兌換紅利點數時呼叫的方法
        if(bonus > 0) {
            this.bonus -= bonus;
        }
        return this.bonus;
    }
    int getBalance() {
        return balance;
    }
    int getBonus() {
        return bonus;
    }
    String getNumber() {
        return number;
    }


給它getBalance()等方法

使用者現在可這樣用:


1
2
3
4


        Scanner sc = new Scanner(System.in);
        CashCard card1 = new CashCard("A001", 500, 0);
        card1.store(sc.nextInt());
        System.out.printf("%d",card1.getBalance());


Java中取值通常開頭取get

最後再來介紹一下class中的成員Field

看下圖:



這是NetBeans IDE 8.1

首先一個class成員包括變數、method、建構子,由上圖來看,上面三個鎖起來的是私有成員、黃色菱形是建構子、紅色的都是方法、其中有一個特別紅色中間畫一條線是static成員



結論:

封裝目的主要就是隱藏物件細節,利用封裝,使用者無需知道類別內部細節,只懂操作及使用就好,private也可用在method和constructor

沒有留言:

張貼留言