Java 入門指南 - 物件的模板,類別

典型的類別 (class) 定義,如下圖




整體結構就是宣告類別,類別名稱之後用一對大括弧把定義圍起來,裡頭有屬性 (field) 、建構子 (constructor) 、方法 (method) 的的定義,如果這個類別還需要是可執行的,就得額外定義 main() 方法。我們將上面的定義例子寫成完整的範例程式,如下
// 宣告類別名稱
public class Demo2 {
    // 這裡定義屬性
    int a;
    int b;
    
    // 這裡定義建構子
    public Demo2(int a, int b) {
        this.a = a;
        this.b = b;
    }
    
    // 這裡定義方法
    public int do_something() {
        return a + b;
    }
     
    // 這裡定義 main() 方法
    public static void main(String[] args) { 
        Demo2 demo = new Demo2(11, 22);
        
        System.out.println();
        System.out.printf("a = %d, b = %d\n", demo.a, demo.b);
        System.out.printf("a + b = %d\n", demo.do_something());
        System.out.println();
    }
}

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


先來編譯執行,看看執行出什麼結果囉



程式的執行在 main() 方法的定義地方
// 這裡定義 main() 方法
public static void main(String[] args) { 
    Demo2 demo = new Demo2(11, 22);
        
    System.out.println();
    System.out.printf("a = %d, b = %d\n", demo.a, demo.b);
    System.out.printf("a + b = %d\n", demo.do_something());
    System.out.println();
}


我們在 main() 中宣告 (declare) 並建立相同 Demo2 型態的物件 demo ,為什麼可以這樣做呢?為什麼要在 main() 中另外建立物件 (object) ,而非直接存取屬性 a 及 b 呢?答案很簡單,首先,因為 a 與 b 沒有給初值,我們當然可以定義後直接給初值,例如
// 這裡定義屬性
int a = 11;
int b = 22;


這樣屬性 a 與 b 就各自擁有初值 11 及 22 ,我們的作法卻是
// 這裡定義屬性
int a;
int b;


屬性通常定義在所有方法之外,通常直接放在大括弧之後,類別宣告的下一行。這裡只有進行屬性的宣告,也就是宣告屬性為 int 型態的變數,注意,這裡我們在 int 之後空一格,然後接上屬性的識別字 (identifier) 名稱。


Java 並沒有嚴格限制程式的編排方式,所以只要合乎語法, int 空一格,或空兩格並沒有多大關係,但須小心,如果把 int 跟識別字連結在一起,如 inta 或 intb ,編譯器會認為這是額外的識別字名稱,並非我們想要的 a 或 b 了。


因為我們的目的是宣告類別 Demo2 有哪些屬性,也就是先宣告屬性的資料型態,以及屬性的識別字名稱,屬性的值實際是由建構子所賦予,這也是建構子的主要功能之一。再來看到建構子定義的地方
// 這裡定義建構子
public Demo2(int a, int b) {
    this.a = a;
    this.b = b;
}


建構子如同方法,但不具有回傳值 (return value) 型態 (type) ,因為建構子的目的就是建立物件 (object) ,因此無須指明回傳值的型態。這裡,建構子有兩個參數 (parameter) ,分別是 a 與 b ,由於名稱和屬性相同,在建構子後面的大括弧,也就是建構子的程式區塊 (block) 中,參數 a 與 b 作為此區塊的區域變數 (local variable) ,屬性名稱 a 與 b 便無法使用,因此我們利用關鍵字 (keyword) this 來存取屬性,並把參數指派 (assignment) 給屬性。


當然,若是屬性名稱與建構子的參數名稱不同,我們可以不用 this 便可直接存取屬性名稱,例如
// 這裡定義建構子
public Demo2(int c, int d) {
    a = c;
    b = d;
}


這樣的小程式中,怎麼寫倒是看個人喜好,往後陸續介紹 publicprivate ,也就是權限方面的修飾詞 (modifier) 之後,我們會提供另外一種寫法。附帶一提, public 是公開的意思, private 則是私有的意思。


Demo2 只有定義一個方法,也就是 do_something()
// 這裡定義方法
public int do_something() {
    return a + b;
}


do_something() 回傳兩個屬性的相加值, return 是關鍵字之一,用來在方法中回傳數值。這裡 + 是 Java 的算術運算子 (arithmetic operator) 之一,用來作加法,因此 a + b 構成一個運算式 (expression) ,運算式會有一個計算結果, return 就是回傳這個計算結果。


注意, + 是鍵盤上直接打出來的半形字元,非全形字元 + 。


方法宣告時,除了回傳值型態,就是小括弧中的參數列 (parameter list) ,若無提供參數,小括弧就應該留空,也就是不填進任何東西,類似的,方法若無回傳值,應該宣告為 void ,像是 main() 就宣告成沒有回傳值的 void


我們回到最早的問題,也就是為什麼可以在 main() 中建立與 Demo2 相同的物件?其實原因很簡單,就是 static 的緣故。 static 中文稱為靜態的,由於屬性與方法在類別中合稱為成員 (member) ,宣告為 static 的成員被稱為 static 成員, static 成員屬於類別,非 static 成員屬於物件。


成員屬於類別跟屬於物件到底有什麼不同呢?差別在, static 成員可以以類別名稱呼叫,我們不是已經用過很多個 System.out.println() 的例子了嗎?
System.out.println()


out 是 System 類別的 static 成員, out 是個 PrintStream 型態的物件,而 println() 為 out 的非 static 成員,其為印出字串 (string) 結尾附加上新行符號的方法。


所以,我們要讓 Demo2 變成可執行的類別,我們就得替 Demo2 加上 main() 方法的定義,由於 main() 屬於 static 的成員, static 成員不能存取非 static 成員,這裡所謂的不能,其實是 Java 的語法規則,所以為了使 Demo2 可以測試 Demo2 自己,語法允許我們可以在 Demo2 的 main() 中建立 Demo2 的物件。


有很難懂嗎?如果我們不想用 Demo2 測試 Demo2 他自己,那我們就得另寫一個含有 main() 的類別,如 Demo3 ,然後在 Demo3 中的 main() 建立 Demo2 的物件
// 測試 Demo2 的類別
public class Demo3 {
    public static void main(String[] args) { 
        Demo2 demo = new Demo2(11, 22);
        
        System.out.println();
        System.out.printf("a = %d, b = %d\n", demo.a, demo.b);
        System.out.printf("a + b = %d\n", demo.do_something());
        System.out.println();
    }
}

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


Demo3 必須和 Demo2 在相同資料夾內,兩個類別檔案也必須分開編譯 (compile) 。嗯, Demo2 、 Demo3 等等講了這麼多名稱,這些都是識別字,我們接下來看看變數命名規則吧!


中英文術語對照
類別class
屬性field
建構子constructor
方法method
宣告declare
物件object
識別字identifier
回傳值return value
型態type
物件object
參數parameter
區塊block
區域變數local variable
關鍵字keyword
指派assignment
修飾詞modifier
算術運算子arithmetic operator
運算式expression
參數列parameter list
成員member
字串string
編譯compile


您可以繼續參考
基礎篇


相關目錄
回 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 月訂正

2 則留言:

Unknown 提到...

out 應該是 System類別的 "物件" 而不是System類別的成員

Kaiching Chang 提到...

類別中的 field 與 method 合稱為成員沒錯,當然這裡也有說明 out 是 PrintStream 型態的物件 ^_^