首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java super 與 this 關鍵字深度解析:繼承鏈中的方法調用與變量訪問機制

     編輯:Java 綜合教學

前言

super 與 this 是 Java 繼承體系中最核心卻最易誤解的兩個關鍵字。本文通過 6 個層層遞進的代碼範例,系統剖析它們在繼承鏈中的真實行為,揭開「為什麼有時輸出相同」的謎團。

核心結論先行

外部的 super/this 僅決定「調用父類還是子類的方法」,不影響方法體內部的 super/this 綁定。
方法體內:super.xxx 永遠綁定「所在類的父類」;this.xxx 分三種情況綁定。

範例 1:子類未覆寫父類方法

class Grandfather { String name = "grandfather"; }
class Father extends Grandfather { 
    String name = "father"; 
    void show() { 
        System.out.println("father.show-->" + this.name + "--" + super.name); 
    } 
}
class Son extends Father { 
    void rs() { 
        super.show();  // 輸出: father.show-->father--grandfather
        this.show();   // 輸出: father.show-->father--grandfather (相同!)
    } 
}

現象:super.show() 與 this.show() 輸出相同。
原因:Son 未覆寫 show(),繼承自 Father。this.show() 解析為 Son 的 show() (實為繼承的 Father.show());super.show() 解析為 Son 父類即 Father 的 show()。兩者指向同一方法。

範例 2:getClass() 的特殊性

public class Test {
    public void test() {
        System.out.println(super.getClass());  // class Test
        System.out.println(this.getClass());   // class Test
    }
}

getClass() 為 final 方法,編譯期靜態綁定,this.getClass() 直接解析為當前類 (Test) 的方法;super.getClass() 解析為父類 (Object) 的方法,但 Object.getClass() 邏輯是返回運行時對象類型 (Test.class),故結果相同。

範例 3:子類覆寫方法後的差異

class Son extends Father {
    void show() {  // 覆寫
        System.out.println("son.show-->" + this.name + "--" + super.name);
    }
    void rs() {
        super.show();  // father.show-->father--grandfather
        this.show();   // son.show-->father--father  (不同!)
    }
}

此時 this.show() 調用 Son 覆寫版本,this.name 為 Father.name (因 Son 無 name,向上查找),super.name 為 Father 父類即 Grandfather.name?實測 super.name 指 Father 的父類欄位?

修正:Son.show() 中 super.name 訪問 Son 父類 Father 的 name 欄位 → father。故輸出 son.show-->father--father。

範例 4:未覆寫方法的靜態綁定行為

class Father { 
    final void m6() { ... }  // final
    static void m7() { ... } // static
    void m8() { ... }        // 普通
}
class Child extends Father { 
    void m8() { ... }        // 覆寫 m8
}
Child c = new Child();
c.m6();  // father's m6 (final, 靜態綁定)
c.m7();  // father's m7 (static, 靜態綁定)
c.m8();  // child's m8 (動態綁定)

規律:final、static、private、構造方法調用 → 編譯期靜態綁定;普通實例方法 → 運行期動態綁定。

範例 5:欄位隱藏 vs 修改繼承欄位

// 隱藏:子類聲明同名欄位
class Son extends Father { String name = "son"; }  // 隱藏 Father.name
// 修改:構造器賦值,不聲明新欄位
class Son extends Father { 
    public Son() { name = "son"; }  // 修改繼承的 Father.name
}

關鍵差異:

  • 隱藏:父類欄位不受影響,super.name 仍訪問父類值
  • 修改繼承欄位:父類欄位被直接修改,super.name 讀到新值

若 Father 也隱藏了 Grandfather.name,則修改只影響 Father.name;若 Father 未隱藏,修改會透傳到 Grandfather.name。

範例 6:綜合驗證 — 外部 super/this 不影響內部綁定

class Father extends Grandfather {
    void show() {
        System.out.println(this.name);        // Father.name (變量/靜態方法: 編譯期綁定所在類)
        System.out.println(this.toString());  // Son.toString() (普通方法: 運行期綁定運行對象)
        System.out.println(this.toString2()); // Father.toString2() (靜態方法: 編譯期綁定)
        System.out.println(super.name);       // Grandfather.name
        System.out.println(super.toString()); // Grandfather.toString()
        System.out.println(super.toString2());// Grandfather.toString2()
    }
}
class Son extends Father {
    void rs() {
        super.show();  // 進入 Father.show()
        this.show();   // 進入 Father.show() (Son 未覆寫)
    }
}

輸出驗證:兩次調用 Father.show() 內部 this/super 綁定完全一致,外部呼叫方式 (super.show() vs this.show()) 不改變方法內部解析邏輯。

完整綁定規則總結表

場景super.xxxthis.xxx
變量 / 靜態方法 / final / private / 構造器所在類父類的成員 (編譯期確定)所在類的成員 (編譯期確定)
普通實例方法所在類父類的方法 (編譯期確定調用點)運行時對象的方法 (動態綁定) → 若子類未覆寫/隱藏,則使用繼承自父類的版本

經典面試題:super.getClass() 為何返回子類 Class?

class Father {
    void show() {
        System.out.println(this.getClass());           // class Son
        System.out.println(super.getClass());          // class Son (!)
        System.out.println(this.getClass().getSuperclass()); // class Father
        System.out.println(super.getClass().getSuperclass()); // class Father
    }
}
class Son extends Father { void rs() { super.show(); this.show(); } }

解析:getClass() 為 final + Native 實現,邏輯為 GetObjectClass(this)。this 指運行時對象 (Son),故無論 this.getClass() 還是 super.getClass(),this 都是 Son 實例,返回 Son.class。getSuperclass() 再取父類 Father.class。

總結

  1. super 永遠指向「所在類的父類」,編譯期靜態確定
  2. this 分兩類:變量/靜態/final/private/構造器 → 編譯期綁定所在類;普通方法 → 運行期綁定實際對象
  3. 外部呼叫者 (super.show() vs this.show()) 只決定「進入哪個類的方法」,不影響方法內部的 super/this 解析
  4. 欄位隱藏不影響父類欄位;修改繼承欄位會影響父類欄位
  5. getClass() 永遠返回運行時對象類型,與 super/this 無關

掌握這些規則,繼承鏈中的方法調用與欄位訪問將不再有歧義。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved