2.4實例和類成員
2.4.1理解實例和類成員
下面詳細討論一下實例和類成員,具體涉及變量和方法以及類變量和方法:
你這樣聲明一個成員變量,比如在類Myclass中有一個float型的aFloat:
class MyClass {
float aFloat;
}
這樣你就聲明一個實例變量。每次你創建一個類的實例的時候,系統就為實例創建了類的每一個實例變量的副本。你可以從對象中訪問對象的實例變量。
實例變量跟類變量是不一樣的,類變量示使用靜態修改量來聲明的。不管類創建了多少個實例,系統為每個類變量分配了類變量。系統為類變量分配的內存是在它第一次調用類的時候發生的。所有的實例共享了類的類變量的相同副本。你可以通過實例或者通過類本身來訪問類變量。
它們的方法是類似的:你的類可以有實例方法和類方法。實例方法是對當前對象的實例變量進行操作的,而且訪問類變量。另外一個方法,類方法不能訪問定義在類中的實例變量,除非它們創建一個新的對象並通過對象來訪問它們。同樣,類方法可以在類中被調用,你不必需要一個實例來調用一個類方法。
缺省地,除非其它的成員被指定,一個定義在類中成員就是一個實例成員。這個在下面定義的類有一個實例變量,有一個整型的x,兩個實例方法x和setX,它們設置其它對象以及查詢x的數值。
class AnIntegerNamedX {
int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
每次你從一個類實例化一個新的對象,你可以得到每個類的實例變量的副本。這些副本都是跟新對象有關系的。因此,每次你從這個類實例化一個新的AnIntegerNamedX對象的時候,你得以得到跟新的AnIntegerNamedX對象有關的新副本。
一個類的所有實例共享一個實例方法的相同的實行;所有的AnIntegerNamedX實例都共享x和setX的相同執行。這裡注意,兩個方法x和setX是指對象的實例變量x。但是,你可能會問:如果所有AnIntergerNamedX共享x和setX的相同執行,會不會造成模稜兩可的狀態?答案當然是:不是。在實例方法中,實例變量的名字是指當前對象的實例變量,假如實例變量不是由一個方法參數來隱藏的。這樣在x和setX中,x就等價於這個x,而不會造成混亂。
對於AnIntegerNamedX外部的對象如果想訪問x,它必須通過特定的AnIntegerNamedX的實例來實現。假如這個代碼片段處在其它對象的方法中。它創建了兩種不同類型的AnIntegerNamedX,它設置了x為不同的數值,然後顯示它們:
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2;
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
這裡注意,代碼使用setX來為myX設置x的值,而直接給anotherX.x指定一個數值。不管用什麼方法,代碼是在操作兩個不同的x副本:一個包含在myX對象中一,另外一個包含在anotherX對象中。其輸出是用以下代碼片段來實現的:
myX.x = 1
anotherX.x = 2
上面代碼顯示了類AnIntegerNamedX的每一個實例有自己實例變量x的副本以及每個x有自己的數值。
你可以在聲明成員變量的時候,指定變量是一個類變量而不是一個實例變量。相似地,你可以指定方法是一個類方法而不是一個實例方法。系統在第一次調用類來定義變量的時候創建了一個類變量的副本。所有的類實例共享了類變量的相同副本。類方法可以只操作類變量,它們不能訪問定義在類中的實例變量。
為了指定一個成員變量為一個類變量,你可以使用static關鍵字。比如,我們可以修改一下上面的AnIntegerNamedX類,使得x變量現在是一個類變量:
class AnIntegerNamedX {
static int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
現在設置它們的x數值並顯示不同的輸出:
myX.x = 2
anotherX.x = 2
這次的輸出不同,是因為x現在是一個類變量,所以就只有這個變量的副本,它是被AnIntegerNamedX的所有實例所共享的,包括myX和anotherX。當你在其它實例中調用setX的時候,你可以為所有的AnIntegerNamedX的所有實例改變x的數值。
同樣,當我們聲明一個方法的時候,你可以指定方法為類方法而不是實例方法。類方法只可以在類變量中進行操作,並且不能訪問定義在類中的所有實例變量。
為了指定方法為類方法,你可以在方法聲明處使用static關鍵字。下面,我們再次來修改AnIntegerNamedX類,使它的成員變量x為一個實例變量,以及它的兩個方法為類方法:
class AnIntegerNamedX {
int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
當你想編譯這個版本的AnIntegerNamedX,編譯器就會顯示如下的錯誤: AnIntegerNamedX.Java:4: Can't make a static reference to
nonstatic variable x in class AnIntegerNamedX.
return x;
^
出現這些錯誤的原因是類方法不能訪問實例變量,除非方法先創建AnIntegerNamedX的一個實例並且通過它來訪問變量。
下面我們修改一下AnIntegerNamedX,讓x變量成為類變量:
class AnIntegerNamedX {
static int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
現在為x設置數值,並打印出x數值:
myX.x = 2
anotherX.x = 2
再一次,我們通過myX來改變x,並將它改變為AnIntegerNamedX的其它實例。
實例成員和類成員之間的另外一個差別是類成員可以從類本身進行訪問。你不必實例化類來訪問它的類成員。下面讓我們編寫一段代碼來直接從AnIntegerNamedX類中訪問x和setX:
. . .
AnIntegerNamedX.setX(1);
System.out.println("AnIntegerNamedX.x = " + AnIntegerNamedX.x());
. . .
值得一提的是,你現在已經不用再創建myX和anotherX了。你可以設置x並直接AnIntegerNamedX類中檢索x。你不能利用實例成員來處理它,你只能從一個對象來調用實例方法並且只可以從對象中訪問實例變量。而你可以從類的實例或者從類本身來訪問類變量和方法。
上