1. 程式人生 > >java中靜態方法中為什麼不能使用this、super和直接呼叫非靜態方法

java中靜態方法中為什麼不能使用this、super和直接呼叫非靜態方法

這個要從java的記憶體機制去分析,首先當你New 一個物件的時候,並不是先在堆中為物件開闢記憶體空間,而是先將類中的靜態方法(帶有static修飾的靜態函式)的程式碼載入到一個叫做方法區的地方,然後再在堆記憶體中建立物件。所以說靜態方法會隨著類的載入而被載入。當你new一個物件時,該物件存在於對記憶體中,this關鍵字一般指該物件,但是如果沒有new物件,而是通過類名呼叫該類的靜態方法也可以。

程式最終都是在記憶體中執行,變數只有在記憶體中佔有一席之地時才會被訪問,類的靜態成員(靜態變數和靜態方法)屬於類本身,在類載入的時候就會分配記憶體,可以通過類名直接去訪問,非靜態成員(非靜態變數和非靜態方法)屬於類的物件,所以只有在類的物件建立(例項化)的時候才會分配記憶體,然後通過類的物件去訪問。

在一個類的靜態成員中去訪問非靜態成員之所以會出錯是因為在類的非靜態成員不存在的時候靜態成員就已經存在了,訪問一個記憶體中不存在的東西當然會出錯。


在《Java程式設計思想》P86頁有這樣一段話:

  “static方法就是沒有this的方法。在static方法內部不能呼叫非靜態方法,反過來是可以的。而且可以在沒有建立任何物件的前提下,僅僅通過類本身來呼叫static方法。這實際上正是static方法的主要用途。”

  這段話雖然只是說明了static方法的特殊之處,但是可以看出static關鍵字的基本作用,簡而言之,一句話來描述就是:

  方便在沒有建立物件的情況下來進行呼叫(方法/變數)。

  很顯然,被static關鍵字修飾的方法或者變數不需要依賴於物件來進行訪問,只要類被載入了,就可以通過類名去進行訪問。

  static可以用來修飾類的成員方法、類的成員變數,另外可以編寫static程式碼塊來優化程式效能。

1)static方法

  static方法一般稱作靜態方法,由於靜態方法不依賴於任何物件就可以進行訪問,因此對於靜態方法來說,是沒有this的,因為它不依附於任何物件,既然都沒有物件,就談不上this了。並且由於這個特性,在靜態方法中不能訪問類的非靜態成員變數和非靜態成員方法,因為非靜態成員方法/變數都是必須依賴具體的物件才能夠被呼叫。

  但是要注意的是,雖然在靜態方法中不能訪問非靜態成員方法和非靜態成員變數,但是在非靜態成員方法中是可以訪問靜態成員方法/變數的。舉個簡單的例子:

  在上面的程式碼中,由於print2方法是獨立於物件存在的,可以直接用過類名呼叫。假如說可以在靜態方法中訪問非靜態方法/變數的話,那麼如果在main方法中有下面一條語句:

  MyObject.print2();

  此時物件都沒有,str2根本就不存在,所以就會產生矛盾了。同樣對於方法也是一樣,由於你無法預知在print1方法中是否訪問了非靜態成員變數,所以也禁止在靜態成員方法中訪問非靜態成員方法。

  而對於非靜態成員方法,它訪問靜態成員方法/變數顯然是毫無限制的。

  因此,如果說想在不建立物件的情況下呼叫某個方法,就可以將這個方法設定為static。我們最常見的static方法就是main方法,至於為什麼main方法必須是static的,現在就很清楚了。因為程式在執行main方法的時候沒有建立任何物件,因此只有通過類名來訪問。

  另外記住,即使沒有顯示地宣告為static,類的構造器實際上也是靜態方法

2)static變數

  static變數也稱作靜態變數,靜態變數和非靜態變數的區別是:靜態變數被所有的物件所共享,在記憶體中只有一個副本,它當且僅當在類初次載入時會被初始化。而非靜態變數是物件所擁有的,在建立物件的時候被初始化,存在多個副本,各個物件擁有的副本互不影響。

  static成員變數的初始化順序按照定義的順序進行初始化。

3)static程式碼塊

  static關鍵字還有一個比較關鍵的作用就是 用來形成靜態程式碼塊以優化程式效能。static塊可以置於類中的任何地方,類中可以有多個static塊。在類初次被載入的時候,會按照static塊的順序來執行每個static塊,並且只會執行一次。

  為什麼說static塊可以用來優化程式效能,是因為它的特性:只會在類載入的時候執行一次。下面看個例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 class  Person{      private  Date birthDate;            public  Person(Date birthDate) {          this .birthDate = birthDate;      }            boolean  isBornBoomer() {          Date startDate = Date.valueOf( "1946" );          Date endDate = Date.valueOf( "1964" );          return  birthDate.compareTo(startDate)>= 0  && birthDate.compareTo(endDate) <  0 ;      } }

  isBornBoomer是用來這個人是否是1946-1964年出生的,而每次isBornBoomer被呼叫的時候,都會生成startDate和birthDate兩個物件,造成了空間浪費,如果改成這樣效率會更好:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Person{      private  Date birthDate;      private  static  Date startDate,endDate;      static {          startDate = Date.valueOf( "1946" );          endDate = Date.valueOf( "1964" );      }            public  Person(Date birthDate) {          this .birthDate = birthDate;      }            boolean  isBornBoomer() {          return  birthDate.compareTo(startDate)>= 0  && birthDate.compareTo(endDate) <  0 ;      } }

  因此,很多時候會將一些只需要進行一次的初始化操作都放在static程式碼塊中進行。

二.static關鍵字的誤區

1.static關鍵字會改變類中成員的訪問許可權嗎?

  有些初學的朋友會將java中的static與C/C++中的static關鍵字的功能混淆了。在這裡只需要記住一點:與C/C++中的static不同,Java中的static關鍵字不會影響到變數或者方法的作用域。在Java中能夠影響到訪問許可權的只有private、public、protected(包括包訪問許可權)這幾個關鍵字。看下面的例子就明白了:

  提示錯誤"Person.age 不可視",這說明static關鍵字並不會改變變數和方法的訪問許可權。

2.能通過this訪問靜態成員變數嗎?

  雖然對於靜態方法來說沒有this,那麼在非靜態方法中能夠通過this訪問靜態成員變數嗎?先看下面的一個例子,這段程式碼輸出的結果是什麼?

1 2 3 4