因為 Java 語言是支援物件導向的程式語言,所以 Java 程式設開發人員最主要的目標之一,就是設計許多的物件來建立系統,或是更具體地解決一個問題,到目前為止,您已經接觸過幾個 Java SE 中的幾個物件,像是陣列、java.lang.String、java.lang.Integer、java.lang.Double 等,這些物件攜帶著比基本資料型態更多的資訊,藉由這些物件之間的交互作用,您可以解決系統所面對的問題。
Java SE 所提供的類別之目的,在於解決程式開發人員常面對的一些問題,讓開發人員無需花費時間精力重複設計這些類別,而是以這些類別為基礎,進一步解決開發人員所面對的需求,而為了解決問題,自行設計類別是必要且基本的,開發人員必須從問題中辨識出各種物件、定義一個類別以在程式中表示這些物件、然後讓這些物件彼此交互以解決問題。
物件導向分析( Object-Oriented Analysis, OOA )是從問題中識別出各個物件、識別出物件的屬性與行為、並設計物件之間交互行為的一種分析方法。Java 語言既然是一種物件導向程式語言,了解基本的物件導向分析方法,對於學習Java 的初學者來說,就成了一個必備的基本能力。
識別物件為了決定問題範圍中的物件,您必須能夠先辨識物件的特性:
重點提示
物件的名字通常是像"帳目"或"襯衫"的名詞,物件的屬性通常也是名詞,例如:"顏色"或"尺寸"。物件的運算通常是動詞,或名詞與動詞的結合,例如:"顯"或"提交訂單"。
當您利用物件導向分析著手處理一個問題時,您對辨別周遭事物的能力將幫助您更容易定義出物件,下圖表現出一個Duke 為物件時的特性:
設計類別辨別物件幫助您更容易為問題域中的物件設計類別( Classes ),或是系統內每一個物件的藍圖。舉例來說,窗戶的製造商通常會為他們將製造的窗戶的樣式設計一個藍圖。這些藍圖定義了在購買時可供選擇的顏色和把手樣式。這些藍圖是各種不同顏色和樣式的窗戶依據的基礎。
在物件導向的術語中,利用類別 (一般的藍圖)所建立的每一個物件 (窗戶),被稱為類別的實體( Instance )。更具體地來說,每一個由類別而建立的物件,其每一個屬性可以擁有特定的狀態(值),但是將有相同的屬性和操作行為。
註
在 American Heritage 字典中,把類別(class)定義為「有某些共同屬性的團體」(“A group whose members have certain attributes in common.”)。
封裝( Encapsulation )是一個物件導向程式的術語,表示物件中資料的藏匿處(一個安全的「容器」),使得這些資料只能透過某些方法來取得。封裝的重要性在於使您的類別較容易被其他的程式設計人員使用,並且避免類別中的某些資料遭受不適當的修改。
下圖利用保險箱表示了一個封裝的概念。其擁有了一個公開的介面(一個密碼鎖),只有正確的操作,才允許存取私用的內容。
對物件的私用資料予以封裝是一個建議,而非實作時必要的作法,在設計上的作法是:對於物件的私用資料,能不直接公開就不直接公開,目的在達到更好的物件權限控制,以提高程式的安全性與穩定性。
在 Java 程式語言中,使用關鍵字 class 來定義類別,類別主要包括兩個部份:類別宣告(Class declaration)與類別本體(Class body)。宣告類別的語法為:
[modifier] class class_identifier { // ..類別本體 }
其中:
您可以使用 public 來修飾類別的權限,表示該類別是一個公開的類別,程式中的任何物件或類別中都可以直接引用該類別,到目前為止您所看到的類別也都是設定為公開的,並且一個公開的類別在檔案主檔名部份,必須與類別的名稱是一致的。
在類別本體中主要包括三個部份:變數( Variable )、建構式( Constructor )、方法( Method )。直接以實例來說明,程式碼 11-1 定義了一個 Hello 類別,當中包括了類別本體的三個主要部份:
public class Hello{ private String name; public Hello(){ name = "nobody"; } public Hello(String one){ name = one; } public void hello(){ System.out.print("Hello "); System.out.println(name); } public void setName(String one){ name = one; } }程式碼 11-1 Hello.java 變數
程式碼 11-1 第 2 行定義了類別中的變數( Variable ),又常稱為值域( Field )或資料成員( Data member ),變數即之前物件導向分析入門所介紹的,表示物件的屬性( 特徵 ):例如大小、名稱、形狀等。例如,某一物件可能擁有一個顏色的屬性。物件所有屬性的值通常被稱為物件現有的狀態。
程式碼第 2 行宣告了一個 name 變數,用來表示 Hello 型態物件所擁有的一個屬性,name 的型態是 String,預設不參考任何物件,在類別本體中宣告變數的話,依資料型態之不同而會有不同的預設值,如表 11-1 所示:
資料型態 | 初始值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0D |
char | \u0000 |
boolean | false |
Object | null |
類別變數 name 在宣告時使用 private 關鍵字修飾,表示這個類別變數只能在物件內部被存取,具體而言就是只能被在類別本體中定義的方法(Method)存取,外部物件無法直接存取 name,這樣的資料成員稱之為私用成員( Private member ),外部物件只能透過公開的(Public)操作介面才可以間接存取到私用成員(如果類別中有定義這樣的操作介面)。
類別變數宣告時的權限修飾是可有可無的,如果不設定的話,預設是在同一個套件管理下的類別都可以存取該類別變數。
建構式建構式( Constructor )是與類別名稱具有相同名稱的特殊方法,它不具有返回值( Return value ),程式碼 11-1 的第 4 行到第 6 行定義了無參數的建構式,第 8 行到第 10 行定義了可接受 String 型態之引數的建構式,建構式會在您使用關鍵字 new 產生一個實例時執行,至於執行哪一個建構式,視您實例化時給定哪一種型態的引數而決定。
例如下面的程式碼片段會建立一個 Hello 類別的實例,程式在執行時發現您實例化時並沒有提供引數,因此實例化時會執行第 4 行到第 6 行所定義的無參數建構式,將 name 參考至"nobody"字串:
Hello hello = new Hello();
下面的程式碼片段會建立一個 Hello 類別的實例,程式在執行時發現您實例化時提供了"Java"作為引數,因此實例化時會執行第 8 行到第 10 行所定義的建構式,因此將 name 參考至"Java"字串實例:
Hello hello = new Hello("Java");
沒有任何參數的建構式稱之為預設建構式 ( Default constructor ),如果您在定義類別的時候,並沒有定義任何的建構式,則在進行編譯時,編譯器會自動為該類別提供一個公開的、無參數的的預設建構式,如果您定義了自己的建構式,則編譯器就不會為您產生預設建構式,此時由於沒有預設建構式,所以您使用 new 建立實例時,一定要使用有參數的建構式。
重點提示
如果您定義了有參數的建構式,即使您不使用預設建構式,在設計上仍建議撰寫一個本體為空的(不包括任何陳述句)的預設建構式。
在建構式前使用了 public 來修飾,表示這是個公開的建構式,任何物件都可以直接實例化該類別,權限修飾是可有可無的,如果省略的話,預設是只有同一套件管理下的物件可以實例化該類別。
方法程式碼 11-1 的第 12 行到第 15 行定義了 hello() 方法(Method),第 17 行到第 19 行定義了 setName()方法,方法是物件擁有的操作能力(Operation),也可表現出物件的行為(Behavior),例如您可以這麼實例化一個 hello 物件,並操作它的 hello()方法:
Hello hello = new Hello("Java"); hello.hello();
程式會執行 hello()中所定義的方法,結果是在螢幕上顯示"Hello Java"的文字。
方法可以返回一個值,表示方法執行完畢後的運算結果,如果撰寫為 void,表示這個方法執行完畢後不必返回任何值。
方法封裝了物件執行某些操作或演算的細節,一個方法被public 關鍵字修飾時,表示它是一個公開的方法,可以被所有的物件直接操作。
方法也可以用 private 修飾,表示它是一個私用的方法,只能在物件內部使用,私用方法通常是演算法中的一小段可重用程式碼,在類別內部定義私用方法以管理這小段可重用程式碼,對於操作公開方法的外部物件來說,不必知道這些私用方法的存在。
透過公開方法完成操作,可實現對演算法的封裝性,即使您改變了某個操作內部的演算法,對操作該方法的物件並不會影響,因為呼叫的公開介面並沒有改變。
方法前的權限修飾可以省略,如果省略的話,預設就是同一個套件管理下的類別才可以呼叫該方法。
實際撰寫個程式來使用程式碼 11-1 所定義的類別,首先您必須編譯完程式碼 11-1,並注意產生的*.class 檔案必須是在 Classpath 設定的路徑之中,接下來您可以撰寫程式碼 11-2 來測試您所撰寫的 Hello 類別:
public class HelloDemo{ public static void main(String[] args){ // 指定引數建立 hello 實例 Hello hello = new Hello("Java"); // 呼叫 hello()方法 hello.hello(); // 呼叫 setName()改變 name 的參考對象 hello.setName("caterpillar"); hello.hello(); } }程式碼 11-2 HelloDemo.java
假設您並不是 Hello 類別的設計人員,今天您只要知道 Hello 類別上有什麼公開方法,就可以直接實例化它並加以操作,您在前幾個單元也就是這麼操作 Java SE 提供的一些物件的,所以設計良好的類別可以提高程式碼的重用性,減輕重複開發相同功能元件的負擔。