Java中關於this、super、static關鍵字的理解

文章目錄

之所以將static與this和super放一起,是想從對象和類的角度去理解,同時也加深對類和對象的理解。

this

如果有兩個同類型的對象,分別叫作a 和b,那麼您也許不知道如何爲這兩個對象同時調用一個 f()方法:

public 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調用的方法或屬性,肯定不能有static修飾符的,因爲static修飾的肯定是屬於類屬性或方法,兩者的存儲位置都不在同一內存中。
假定我們在一個方法的內部,並希望獲得當前對象的句柄。由於那個句柄是由編譯器“祕密”傳遞的,所以沒有標識符可用。這個時候就可以用this關鍵字,this 關鍵字(注意只能在非靜態方法內部使用)可爲已調用了其方法的那個對象生成相應的句柄。可象對待其他任何對象句柄一樣對待這個句柄。很多時候,你不用this編譯器也能明白,但也有特殊情況,比如需要返回當前對象的時候。
有以下可能會用到this:

  1. 當在一個非靜態的方法中想要使用該方法所處類的非靜態屬性或方法時,可以使用this調用,也可以不寫,編譯器會默認調用當前對象。
  2. 當在某個非靜態方法中想要使用當前對象或者返回當前對象時,使用this。
  3. 在構造函數內部使用this調用另一個構造函數,以此來減少代碼量,注意調用的代碼應放在構造函數的第一行。這裏注意既然只能放在第一行,那就說明在一個構造函數內部,通過this調用另一個構造函數時,只能調用一個,反應過來了嗎?如果調用兩個,怎麼保證this調用的構造函數放在第一行。
  4. 當構造函數的形參變量和類屬性變量名相同時,要在類屬性變量名前加個this,加以區分,否則編譯器會發出警告。

super

super關鍵字是父類對象的一個引用,只有在繼承關係中,纔會使用這個關鍵字,下面對這種使用做個總結:

  1. 子類繼承父類的屬性和方法,並重寫父類的方法,子類修飾符不能比父類方法的修飾符的範圍狹窄。 如果你用子類引用,子類實例,範圍小不會影響。如果你用父類引用,子類實例,範圍小的話會和多態的衝突,所以是行不通的。

  2. 子類繼承父類,子類必須在構造函數中的第一行使用super()來調用父類的構造函數,雖然你有時候沒有寫也能編譯通過,那是系統默認調用了super。
    關於super的調用,有以下說明:

    • 父類沒有寫構造函數(系統默認有一個無參構造函數),或者父類只有一個無參構造函數,則:子類可以不寫構造函數(可以理解爲:子類的系統默認構造函數,默認調用了super();),也可以寫有無參或者有參構造函數,而且不用調用super,系統會默認調用。
    • 如果父類有有參構造函數,沒有無參構造函數,則:子類不能有無參構造函數,可以有有參構造函數且必須在有參構造函數中顯示的調用父類的有參構造函數,即super(參數名)。
    • 如果父類既有有參構造函數,又有無參構造函數,則:子類可以有有參也可以有無參,且可以使用super調用父類的有參無參構函數,也可以不調用super,使用this.屬性 = 形參,的方式去實現有參構造函數。
    • 如果父類的構造函數只有一個,且修飾符是private,則:不可以被繼承。

static

理解了 this 關鍵字後,我們可更完整地理解 static(靜態)方法的含義。

通常,我們創建類時會指出那個類的對象的外觀與行爲。除非用new 創建那個類的一個對象,否則實際上並未得到任何東西。只有執行了 new 後,纔會正式生成數據存儲空間,並可使用相應的方法。
但在兩種特殊的情形下,上述方法並不堪用:

  • 一種情形是只想用一個存儲區域來保存一個特定的數據——無論要創建多少個對象,甚至根本不創建對象。這個有點像C語言中的全局變量,任何類都可以引用,而且引用的是同一內存下面的東西。
  • 另一種情形是我們需要一個特殊的方法,它沒有與這個類的任何對象關聯。也就是說,即使沒有創建對象,也需要一個能調用的方法。

爲滿足這兩方面的要求,可使用static(靜態)關鍵字。一旦將什麼東西設爲static,數據或方法就不會同那個類的任何對象實例聯繫到一起。所以儘管從未創建那個類的一個對象,但仍能調用一個 static 方法,或訪問一些 static 數據。

而在這之前,對於非 static 數據和方法,我們必須創建一個對象,並用那個對象訪問數據或方法。這是由於非static 數據和方法必須知道它們操作的具體對象。當然,在正式使用前,由於static 方法不需要創建任何對象,所以它們(靜態)不可簡單地調用其他(非靜態)那些成員,同時不引用一個已命名的對象,即不能直接訪問非 static 成員或方法(因爲非static 成員和方法必須同一個特定的對象關聯到一起)。

例如,下述代碼能生成一個 static數據成員,並對其初始化:

class StaticTest {
	Static int i = 47;
}

現在,儘管我們製作了兩個StaticTest 對象,但它們仍然只佔據StaticTest.i 的一個存儲空間。這兩個對象都共享同樣的i。請考察下述代碼:

StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();

此時,無論 st1.i 還是 st2.i 都有同樣的值 47,因爲它們引用的是同樣的內存區域。

有兩個辦法可引用一個 static 變量。

  • 正如上面展示的那樣,可通過一個對象命名它,如st2.i
  • 亦可直接用它的類名引用,如StaticTest .i,而這在非靜態成員裏是行不通的(最好用這個辦法引用static 變量,因爲它強調了那個變量的“靜態”本質)。

類似的邏輯也適用於靜態方法。既可象對其他任何方法那樣通過一個對象引用靜態方法,亦可用特殊的語法
格式“類名.方法()”加以引用。靜態方法的定義是類似的:

class StaticFun {
	static void incr() {
		StaticTest.i++; //此時,無論 st1.i 還是st2.i 的值都是48。
	}
}

從中可看出,StaticFun 的方法 incr()使靜態數據 i 增值。通過對象,可用典型的方法調用incr()

StaticFun sf = new StaticFun();
sf.incr();

或者,由於 incr()是一種靜態方法,所以可通過它的類直接調用:

StaticFun.incr();

儘管是“靜態”的,但只要應用於一個數據成員,就會明確改變數據的創建方式(一個類一個成員,以及每個對象一個非靜態成員)。若應用於一個方法,就沒有那麼戲劇化了。

對方法來說,static 一項重要的用途就是幫助我們在不必創建對象的前提下調用那個方法。這一點是至關重要的——特別是在定義程序運行入口方法 main()的時候。和其他任何方法一樣,static 方法也能創建自己類型的命名對象。所以經常把 static 方法作爲一個“領頭羊”使用,用它生成一系列自己類型的“實例”。

有些人抱怨 static 方法並不是“面向對象”的,因爲它們具有全局函數的某些特點;利用 static 方法,我
們不必向對象發送一條消息,因爲不存在 this。這可能是一個清楚的自變量,若您發現自己使用了大量靜態
方法,就應重新思考自己的策略。然而,static 的概念是非常實用的,許多時候都需要用到它。

【注】:上面的很多內容都是出自《Java編程思想》第四版,大家都說這一本書寫的很好,我是第一次看這本書,也是剛看,所以感受不是很深,但把《Java核心技術 卷一》看了好幾遍了,感覺有些東西還是沒有講到我想要的深度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章