如果有兩個同類型的對象,分別叫作a和b,那麼您也許不知道如何為這兩個對象同時調用一個f()方法:
class Banana { void f(int i) { /* ... */ } }
Banana a = new Banana(), b = new Banana();
a.f(1);
b.f(2);
若只有一個名叫f()的方法,它怎樣才能知道自己是為a還是為b調用的呢?
為了能用簡便的、面向對象的語法來書寫代碼——亦即“將消息發給對象”,編譯器為我們完成了一些幕後工作。其中的秘密就是第一個自變量傳遞給方法f(),而且那個自變量是准備操作的那個對象的句柄。所以前述的兩個方法調用就變成了下面這樣的形式:
Banana.f(a,1);
Banana.f(b,2);
這是內部的表達形式,我們並不能這樣書寫表達式,並試圖讓編譯器接受它。但是,通過它可理解幕後到底發生了什麼事情。
假定我們在一個方法的內部,並希望獲得當前對象的句柄。由於那個句柄是由編譯器“秘密”傳遞的,所以沒有標識符可用。然而,針對這一目的有個專用的關鍵字:this。this關鍵字(注意只能在方法內部使用)可為已調用了其方法的那個對象生成相應的句柄。可象對待其他任何對象句柄一樣對待這個句柄。但要注意,假若准備從自己某個類的另一個方法內部調用一個類方法,就不必使用this。只需簡單地調用那個方法即可。當前的this句柄會自動應用於其他方法。所以我們能使用下面這樣的代碼:
class Apricot {
void pick() { /* ... */ }
void pit() { pick(); /* ... */ }
}
在pit()內部,我們可以說this.pick(),但事實上無此必要。編譯器能幫我們自動完成。this關鍵字只能用於那些特殊的類——需明確使用當前對象的句柄。例如,假若您希望將句柄返回給當前對象,那麼它經常在return語句中使用。
//: Leaf.java // Simple use of the "this" keyword public class Leaf { private int i = 0; Leaf increment() { i++; return this; } void print() { System.out.println("i = " + i); } public static void main(String[] args) { Leaf x = new Leaf(); x.increment().increment().increment().print(); } } ///:~
由於increment()通過this關鍵字返回當前對象的句柄,所以可以方便地對同一個對象執行多項操作。
1. 在構建器裡調用構建器
若為一個類寫了多個構建器,那麼經常都需要在一個構建器裡調用另一個構建器,以避免寫重復的代碼。可用this關鍵字做到這一點。
通常,當我們說this的時候,都是指“這個對象”或者“當前對象”。而且它本身會產生當前對象的一個句柄。在一個構建器中,若為其賦予一個自變量列表,那麼this關鍵字會具有不同的含義:它會對與那個自變量列表相符的構建器進行明確的調用。這樣一來,我們就可通過一條直接的途徑來調用其他構建器。如下所示:
//: Flower.java // Calling constructors with "this" public class Flower { private int petalCount = 0; private String s = new String("null"); Flower(int petals) { petalCount = petals; System.out.println( "Constructor w/ int arg only, petalCount= " + petalCount); } Flower(String ss) { System.out.println( "Constructor w/ String arg only, s=" + ss); s = ss; } Flower(String s, int petals) { this(petals); //! this(s); // Can't call two! this.s = s; // Another use of "this" System.out.println("String & int args"); } Flower() { this("hi", 47); System.out.println( "default constructor (no args)"); } void print() { //! this(11); // Not inside non-constructor! System.out.println( "petalCount = " + petalCount + " s = "+ s); } public static void main(String[] args) { Flower x = new Flower(); x.print(); } } ///:~
其中,構建器Flower(String s,int petals)向我們揭示出這樣一個問題:盡管可用this調用一個構建器,但不可調用兩個。除此以外,構建器調用必須是我們做的第一件事情,否則會收到編譯程序的報錯信息。
這個例子也向大家展示了this的另一項用途。由於自變量s的名字以及成員數據s的名字是相同的,所以會出現混淆。為解決這個問題,可用this.s來引用成員數據。經常都會在Java代碼裡看到這種形式的應用,本書的大量地方也采用了這種做法。
在print()中,我們發現編譯器不讓我們從除了一個構建器之外的其他任何方法內部調用一個構建器。
2. static的含義
理解了this關鍵字後,我們可更完整地理解static(靜態)方法的含義。它意味著一個特定的方法沒有this。我們不可從一個static方法內部發出對非static方法的調用(注釋②),盡管反過來說是可以的。而且在沒有任何對象的前提下,我們可針對類本身發出對一個static方法的調用。事實上,那正是static方法最基本的意義。它就好象我們創建一個全局函數的等價物(在C語言中)。除了全局函數不允許在Java中使用以外,若將一個static方法置入一個類的內部,它就可以訪問其他static方法以及static字段。
②:有可能發出這類調用的一種情況是我們將一個對象句柄傳到static方法內部。隨後,通過句柄(此時實際是this),我們可調用非static方法,並訪問非static字段。但一般地,如果真的想要這樣做,只要制作一個普通的、非static方法即可。
有些人抱怨static方法並不是“面向對象”的,因為它們具有全局函數的某些特點;利用static方法,我們不必向對象發送一條消息,因為不存在this。這可能是一個清楚的自變量,若您發現自己使用了大量靜態方法,就應重新思考自己的策略。然而,static的概念是非常實用的,許多時候都需要用到它。所以至於它們是否真的“面向對象”,應該留給理論家去討論。事實上,即使Smalltalk在自己的“類方法”裡也有類似於static的東西。