首頁 >Java >java教程 >詳解Java關鍵字this(動力節點整理)

詳解Java關鍵字this(動力節點整理)

黄舟
黄舟原創
2017-03-31 10:28:271448瀏覽

java中的this隨處可見,用法也多。通常情況下理解this關鍵字還是很容易的,但是在我初學的時候,有一個疑問卻一直不能很清晰的理解,現在慢慢的理解了,下面透過本文給大家記錄下,有需要的朋友參考下方

我們通常在用Java中的this關鍵字的時候,都知道this是代表正在呼叫這個類別的方法的目前實例。通常情況下理解this關鍵字還是很容易的,但是在我初學的時候,有一個疑問卻一直不能很清晰的理解,現在慢慢的理解了,就想把它記下來,也許有人和我有相同的疑問,說不定可以幫助別人。我們還是先簡單的看看通常情況下this的作用吧。例如下面的程式碼:

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().print();
 }
}

在Leaf類別的main方法中,我們new了一個Leaf實例 x,然後x實例呼叫increment()方法。如果increment()是普通的方法或void方法,這個地方就沒有什麼值得我們研究的了。特殊的是,在increment()方法中,我們return的是一個this,這個this代表的就是我們剛剛建立的x。因為x正在呼叫increment()方法,所以,increment()方法this就很明顯代表的是Leaf的x實例了。

     這看起來沒有什麼可討論的,this就是代表的呼叫該方法的實例x。可是,假如我們把main()函數修改成下面的樣子

public static void main(String[] args) {
 Leaf x = new Leaf();
 x.increment().increment().print();
  
 Leaf y = new Leaf();
 y.increment().increment().print();
}

以上修改的程式碼中,我們增加創建了一個Leaf實例y,然後y也連續呼叫呼叫了兩次increment()。現在問題來了,假如 x,y同時呼叫的increment()方法,那麼this到底能代表誰呢?你可能會覺得這有什麼問題,x呼叫increment()方法,this就代表x, y呼叫increment()方法,this就代表y。可問題是,當我們講呼叫方法的時候,在jvm層面上是找到Leaf類別中increment()方法所在的記憶體位址,然後在java虛擬機棧中建立堆疊幀.

然後在堆疊幀中執行方法裡面的程式碼。現在看到了吧,也就是說,在jvm執行方法層面,沒有所謂的x調用,y調用了,那麼,方法中的this到底是怎麼確定指向哪個實例的呢?

      我們還是來看看Leaf類別字節碼中是怎麼展示的,是不是我們漏了什麼,如果我們沒有把x實例或是y實例傳遞到方法裡面去,那麼,在jvm執行方法的時候,是不可能知道this具體指向哪個實例的。

到這裡,我們看到在increment()方法中,編碼中沒有參數,但是在字節碼裡面卻顯示參數個數為1,仔細想想,結果已經很明顯了:jvm在執行編譯的時候,在實例方法中,會預設隱藏的傳遞一個參數,這個參數就是目前呼叫的實例本身。例如x調用,隱藏就把x傳過去,y調用,就把y傳過去。所以,我們的this才能在jvm執行方法層面確定到底指向的是誰。

上面的結論是我們自己推論出來的,有沒有那本書對這個有詳細的描述呢? 《java程式思想》裡面,對這塊是這樣描述的:

假定我們在一個方法的內部,並希望獲得當前物件的句柄。由於那個句柄是由編譯器「秘密」傳遞的,所以沒有識別符可用。然而,針對這項目的有個專用的關鍵字:this。

在裡面講的這個編譯器秘密傳遞的句柄,就是我們這裡的這個隱藏參數。

    到此為止,關於this的描述想必已經很清楚了,我們在jvm層面對它進行了理解。那麼,各位有沒有興趣在看下下面的這個例子,想想這個基類B中的this代表了什麼呢?

public class B {
 public B() {
  System.out.println(this.getClass().getSimpleName()); 
  System.out.println(((A) this).a); 
 }
}
public class A extends B {
 public int a = 100;  
 public A() {
  a = 200;
 } 
 public static void main(String[] args) {
  new A();
 }
}

這個例子原本是為了了解java有繼承結構的時候類別是怎麼完成初始化的,可是這裡面的B類別中的建構子比較特殊:B類別中的建構子中的this輸出的SimpleName是A。通常我們遇到的情況,B類中的this輸出的SimpleName應該是B,但這裡卻是A?為什麼?

在上面我們講this的過程中,其實已經涉及到這塊了,在呼叫java方法創建堆疊幀的時候,jvm會秘密的傳遞一個當前實例。所以,當我們在執行A的建構子的時候,預設會呼叫父類別B的建構函數,在呼叫父類別B建構子的時候,秘密的傳進去的目前實例是A的實例----因為是在A的建構函數中呼叫的B,所以,這個地方的this反而代表了A。

以上是詳解Java關鍵字this(動力節點整理)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn