基礎程式設計(14)-物件、屬性與方法

隨著電腦運算能力的進步,現在的程式大多屬於物件導向程式設計,即使是腳本語言也會被要求”模組化”的設計,因此物件導向程式語言的設計概念已經變成程式設計的基礎知識了。所謂的模組化設計,其實就是撰寫”黑盒子“,而這個黑盒子可能是傳統的函數與副程式,或是本章要介紹的物件。在物件導向程式語言裡,程式的最大單位是”物件”,而所有的函數與副程式都被稱為”方法”,用法是類似的,但是概念卻不太一樣。物件導向程式設計有很多專有名詞,不過我個人覺得這些名詞盡是些擾人耳目的東西,只會讓初學者越學越混亂而已,所以我會盡量不去提它,重點是看懂內容。

物件封裝(Encapsulation)

所謂的「物件(Object)」是指程式設計師利用程式”定義”的一個”存在於電腦中的”物體,這個物體可大可小。我們用角色扮演的遊戲來舉例,每一個玩家扮演的角色、任何一個NPC,或是任一個裝備道具,都代表著不同的物件。所以一個遊戲(程式)就是由無數個物件組合而成的。

遊戲裡的物件五花八門,但是從程式設計的角度來看,物件裡只有兩種東西:屬性(Property)與方法(Method)。聽起來是不是有點不可思議呢?屬性的正式名稱是成員資料(Data Member),但我個人認為叫屬性比較簡單易懂。如果學過HTML的人就知道,一個節點(Tag)的後面會跟著一些屬性。我們可以把一個節點當成是一個物件,而屬性就是它附加的參數。不過當然了,物件導向語言的屬性可不單純只有文字和數字,它的屬性也可能是另外一個物件,所以要比HTML複雜得多了。

物件屬性到底是什麼意思?想想我們角色扮演遊戲裡的人物,是不是會有一些基本數值,以及已經穿在身上的裝備呢?這些都是我們這個角色(物件)的屬性。從這裡也可以看得出來,屬性並非一成不變的。不過遊戲裡如果有很多職業,每種職業有固定的初始值,那麼這個屬性可能就是固定的,而且相同職業的人都會有一樣的屬性。

接著要來介紹方法了,方法可以說是物件所要執行的動作。在函數與副程式的介紹裡,有提到方法的使用方式和函數副程式是相同的,而它的概念是什麼呢?我們繼續以遊戲來舉例。比如說當角色升級的時候,除了更新角色數據之外,可能還必須要開放新技能,以及顯示升級時的特效。這些不可能是用屬性來處理(屬性代表的只是單純的資料),而是要另外寫一個函數副程式來處理。不過和傳統函數副程式不同的地方在於,這是角色(物件)的動作(方法),兩者的關係非常密切。所以無論是屬性或方法,都是物件這個黑盒子裡的一員。

一般來說,程式的撰寫不建議直接在物件黑盒子外面直接修改屬性,而是要透過方法來修改。整個物件就像我們遊戲裡的角色一樣是個完整的”物體”,這樣的設計就是物件導向裡所謂的”封裝”了。

Java物件宣告

在Java語言裡,「類別(Class)」是用來定義基礎物件的程式碼。簡單來說,在我們創立遊戲角色之前,總是有個基礎原型,大家都是用它來創立角色,一旦角色(物件)創立之後,就會有各自不同的發展。所以類別可以說是物件的原型。接下來我們用一個程式碼來說明Java物件的基本觀念,請跟著下面說明一起閱讀程式碼:

class role    //宣告role類別(物件原型)
{
    private static int baseStrength = 10;       //私人的靜態屬性
    private int strength = baseStrength;        //私人屬性

    static int baseIntelligence = 8;        //公開的靜態屬性
    public int intelligence = baseIntelligence; //公開屬性

    public void upgrade() {     //物件方法,void表示沒有回傳值
        strength += 2;
        intelligence += 1;
    }
    public void print() {       //物件方法
        System.out.println("Attributes: STR " + strength + " INT " + intelligence);
    }
}

public class test   //與檔名相同名稱的物件
{
    //main方法是程式的入口,執行程式從這裡開始
    public static void main(String[] args)  //這一行是固定寫法
    {
        role r1 = new role();   //建立role的副本
        role r2 = new role();   //宣告語法: 類別 物件 = new 類別();

        //先印出物件初始值
        System.out.println("START");
        System.out.print("role 1 ");
        r1.print();         //使用 物件名稱.公開方法 執行動作
        System.out.print("role 2 ");
        r2.print();

        //更新物件屬性
        System.out.println("UPDATE");
        r1.upgrade();
        r1.upgrade();
        r2.upgrade();

        //印出物件更新後的屬性值
        System.out.print("role 1 ");
        r1.print();
        System.out.print("role 2 ");
        r2.print();
        System.out.println();

        //印出靜態屬性初始值
        System.out.println("STATIC INITIAL VALUE");
        System.out.println("r1.baseIntelligence = " + r1.baseIntelligence); //使用 物件名稱.公開屬性 讀取屬性值
        System.out.println("r2.baseIntelligence = " + r2.baseIntelligence);

        //修改靜態屬性後印出
        System.out.println("STATIC CHANGED");
        r1.baseIntelligence = 6;
        System.out.println("r1.baseIntelligence = " + r1.baseIntelligence);
        System.out.println("r2.baseIntelligence = " + r2.baseIntelligence);
    }
}

首先看到第 1 行和第 18 行的程式碼,這二行都寫了一個關鍵字”class”,表示後面大括號{}括起來的內容就是前面類別的內容。事實上,整段程式碼從此一分為二了,這也代表一個.java的檔案裡可以有好幾個class。在每個.java檔裡,一定要有一個類別名稱和檔名一樣,並且加上 public 的修飾字,所以 18 行會比第 1 行多了一個 public。

在執行程式之前,我們先看看 role 類別在做些什麼事。首先,3-7 行宣告了 4 個屬性,然後第 9 行的方法更新了屬性值,第 13 行的方法則是印出屬性值。在這些屬性方法的前面,有 private、public、static 的修飾字,用來宣告它們存取的方式。private 和 public 比較容易理解,如果是 public 表示任何人都可以存取,而 private 只有物件成員可以存取。所以第 4 行的 strength 可以在第 10、14 行被存取,但卻不能在 18 行以後的 test 類別中存取。

object_example

上面是程式執行的結果,也就是 21-56 行 main 方法輸出的結果。首先看到 23、24 行宣告了二個 role 物件(創立二個角色),也就是說物件 r1 和 r2 是類別 role 的副本。26-31 行輸出了二個物件的初始值,我們可以看到它們的初始值一開始是一樣的。在 33-37 行中,r1 升級了二次,r2 只升級一次,所以 39-44 行印出時,我們看到 r1 的數值比 r2 高了。這裡說明了物件與類別之間的關係。

最後一個比較難懂的是 static 修飾字。就如同前面說的,有些屬性可能是所有角色都共用的,所以如果要修改的話,就是全部的角色一起改。這種屬性可以使用 static 修飾字,表示所有的角色都存取同一個變數,不會像前面角色有各自不同數值的情形。46-49 行印出了 baseIntelligence 的初始值,53 行我們只修改 r1 的 baseIntelligence,可是 54、55 行印出的時候,兩個物件的數值卻都是一樣的了,這就是 static 的作用。

  • private:只有相同”物件”的成員可以存取屬性或方法。
  • public:任何物件都可以存取屬性或方法。
  • static:不會產生副本的屬性或方法,表示所有物件都存取同一份屬性或方法。

PS. 這段程式最後修改了 baseIntelligence,但是 r1 和 r2 的 intelligence 仍然是 10 和 9,因為宣告結束後,intelligence 就和 baseIntelligence 沒有關係了。