2010年7月8日 星期四

Java 物件導向觀念 #2



※ 封裝

所謂物件導向程式,主要是由許多不同功能的物件組合而成,而其中的組合關係可能相當的錯綜復雜,若每個物件之間的變數都能夠直接任意的存取使用,很容易就會造成物件內部參數的錯亂,為了避免這類的情況發生,最好的方式就是將物件內的變數都封裝起來,並提供一個單一對外存取的窗口,任何物件要存取這個變數都必須透過這個窗口,而這個窗口就是利用函式(方法)來包裝變數,如此一樣便能讓程式運作更加的穩定與安全。事實上物件裡所包含的結構及方法,本身也是透過物件包裝起來,這也算是封裝特性的一種。所以我們可以利用封裝的特性將物件實作的方法與介面分開,防止物件本身的一些屬性被使用者無意間的修改。


◎ 類別成員(Class member)
在類別中可定義Field成員及方法(Method) 成員。

package com.ittraining.example;

public class Student {

private int classID; // 學號

protected int classID2;

private String name; // 名稱

private String schoolName;//學校

public void setClasssID(int classID) {

this.classID = classID;

}

public void setName(String name) {

this.name = name;

}

public int getClassID() {

return classID;

}

public String getName() {

return name;

}

public void setSchoolName(String schoolName){

this.schoolName=schoolName;

}

public String getSchoolName(){

return schoolName;

}

public void printData(){

System.out.println("姓名:"+name+"\n學號:"+classID+"\n學校:"+schoolName);

}

}



在Java 中,類別的存取權限修飾詞有"public"、"protected"、"private"、"default(無修飾詞)",當修飾詞或空時,預設將會以以套件 (package)為可存取範圍,也就是說只有在同一層package 中的class 才能進行存取。

上述的範例程式中程式中,我們定義了一個Student 類別,其中包含3 個被宣告為private的field 成員,分別是classID 、name與schoolName。由於宣告為private,所以代表這3 個成員只能在同一個Object 中進行存取,而無法被其他物件直接進行存取。

接著我們定義了6個Method 成員,全部都是宣告為"public",代表這些method 是公開的,可被其他物件所呼叫,method 的定義方式如下 :

存取修飾 傳回值型態 方法名稱(參數列) {
  return 傳回值;
}



也就是我們將classID 與name 成員變數透過private 宣告進行封裝,並另外再建立四個公開的方法讓其他物件透過我們公開的方法去對classID 與name 進行存取,而非直接將classID 與name 成員公開出去,一切的資料存取都需透過method 成員呼叫,而非直接呼叫該field 資料成員來進行存取。我們稱之為“資料的封裝",封裝的好處在於可以確保資料的安全性,與存取介面的一致性。



◎ 靜態成員(Static member)
對於每個物件來說,每個物件都各自擁有自己的資料成員。就算兩個物件是由同一個類別透過new 關鍵字產生,也不會發生更改了A Object 內的成員變數,連帶著B Object 也同時變動的情況。因為就在Object 被class loader 載入的那一刻,資料成員就以複本(Copy)的型態載另到記憶體之中。

然而在某些時候你會想要這些物件都可以共享相同的資料成員,此時就需要將刻資料成員宣告為static。一但資料成員被宣告為static 後,在同一個類別之中,該資料就只會存在一份,object 被class loader 載入時並不會再copy static 成員到記憶體中,而是共享的。舉個例來說,我們將前一個Student 的範例中進行些小修改,填加幾個預設學校名稱的參數,並宣告為static。


package com.ittraining.example;

public class Student {

private static String SCHOOL_ONE="建國中學";

private static String SCHOOL_TWO="北一女中";

private static String SCHOOL_THREE="景美女中";

private int classID; // 學號

private String name; // 名稱

private String schoolName="建國中學";//學校

public void setClasssID(int classID) {

this.classID = classID;

}

public void setName(String name) {

this.name = name;

}

public int getClassID() {

return classID;

}

public String getName() {

return name;

}

public void setSchoolName(String schoolName){

this.schoolName=schoolName;

}

public String getSchoolName(){

return schoolName;

}

public void printData(){

System.out.println("姓名:"+name+"\n學號:"+classID+"\n學校:"+schoolName);

}

public static void main(String args[]){

Student st1 =new Student();

st1.setClasssID(12345);

st1.setName("Kyle");

st1.setSchoolName(Student.SCHOOL_ONE);

     st1.printData();

}

}




由於static 成員是直接屬於類別,並不屬於任一個物件所獨有,因此可以直接以類別名稱去進行存取,而無需實體化成物件。例如:

st1.setSchoolName(Student.SCHOOL_ONE);


即有靜態資料,相對的我們也可以將方法宣告成靜態的方法(static method),而靜態方法通常是為了提供一些簡單的常數運算或一些操作上的小工具,例如我們可以於Student.java 中新增一個help 的靜態方法,該方法可說明此類別的功能與使用方式。


public static void help(){

System.out.println("請輸入姓名、學號與、所屬學校");

}



靜態方法的呼叫方式與靜態資料相同,皆是直接透過類別名稱+.運算子 + 方法名。其實我們時常用到System.out.println()本身就是一個靜態的資料+靜態方法的應用,這也就是為什麼我們不必去new System 物件就可以宜接使用out 這個靜物件,並可以直接呼叫println()這個靜態方法將字串貼於console 上。

由於static 成員是屬於類別而不是物件,因此若你要在static method 中呼叫非static 資料,在編譯時就會出現以下的錯誤訊息:

non-static variable xxx cannot be referenced from a static context

同樣的你也不能在static method 中呼叫非static 的method

non-static method xxxx() cannot be referenced from a static context

由於static 成員是屬於類別而不是物件,所以當您呼叫static 方法時,並不會傳入物件的位置參考,所以static方法中不會有 this 參考,由於沒有this 參考,所以在Java 的static 方法成員中不允許使用非static 成員,因為程式沒有this 來參考至物件位址,也就無法辨別要存取哪一個物件的成員,事實上,如果您在static 方法中使用非static 資料成員,在編譯時就會出現以下的錯誤訊息:
non-static variable test cannot be referenced from a static context

或者是在static 函式中呼叫非static 函式,在編譯時就會出現以下的錯誤訊息:
non-static method showMe() cannot be referenced from a static context

最後如果你希望在載入類別時就先進行一些初始化的動作,此時你可以使用static 區塊,將所有需要做初始化資源的成員全都放在static 區塊中。Static 區塊會在第一次呼叫而被載入時,static 區塊中的程式碼會先被執行,且只會執行一次。

public class Student {
....
static {
//
初始化程式碼
}
....
}




沒有留言:

張貼留言