首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Java 經典面試題:繼承與多態下的構造器調用順序分析

     編輯:程式設計疑難解答

題目代碼

public class Apple extends Fruit {
    private String name = "apple";
    
    public Apple() {
        tellName();
        printName();
    }
    
    public void tellName() {
        System.out.println("Apple tell name: " + name);
    }
    
    public void printName() {
        System.out.println("Apple print name: " + name);
    }
    
    public static void main(String[] args) {
        new Apple();
    }
}

class Fruit {
    private String name = "fruit";
    
    public Fruit() {
        tellName();
        printName();
    }
    
    public void tellName() {
        System.out.println("fruit tell name: " + name);
    }
    
    public void printName() {
        System.out.println("fruit print name: " + name);
    }
}

輸出結果

Apple tell name: null
fruit tell name: fruit
Apple print name: null
fruit print name: fruit

詳細執行流程分析

  1. 進入 main,執行 new Apple()。
  2. 調用 Apple() 構造器:
    • 隱式調用父類構造器 super()(即 Fruit())。
  3. 執行 Fruit() 構造器:
    • 調用 tellName() → 多態調用子類重寫方法 → Apple.tellName()。
    • 此時 Apple.name 尚未初始化(默認 null),輸出 Apple tell name: null。
    • 調用 printName() → 多態調用 Apple.printName(),輸出 Apple print name: null。
  4. 返回 Apple() 構造器:
    • 執行 Apple.name = "apple"(實例變量初始化)。
    • 調用 tellName() → Apple.tellName(),輸出 Apple tell name: apple。
    • 調用 printName() → Apple.printName(),輸出 Apple print name: apple。

關鍵知識點總結

階段動作Apple.name 值調用方法版本
1父類構造器開始null (默認值)子類版本 (多態)
2父類構造器調用方法null子類版本
3子類字段初始化"apple"-
4子類構造器調用方法"apple"子類版本

核心規則

  • 構造器調用鏈:子類構造器首行隱式調用 super(),父類構造器優先執行。
  • 多態生效時間:對象創建後即生效,父類構造器中調用的非 final/private 方法會被子類重寫版本覆蓋。
  • 字段初始化順序:父類字段 → 父類構造器體 → 子類字段 → 子類構造器體。
  • 字段無多態:name 是字段而非方法,父類方法訪問的是父類字段,子類方法訪問子類字段(隱藏而非覆蓋)。

最佳實踐建議

  1. 構造器中避免調用可被覆蓋的方法:將初始化邏輯放入單獨的 init() 方法,構造器完成後顯式調用。
  2. 方法標記 final/private:若構造器必須調用某方法,標記為 final 或 private 防止被覆蓋。
  3. 字段區分命名:避免父子類同名字段,使用 this.name / super.name 明確指向。

修正版代碼示例

class Fruit {
    private String name = "fruit";
    
    public Fruit() {
        init();  // 統一初始化入口
    }
    
    protected final void init() {  // final 防止被覆蓋
        tellName();
        printName();
    }
    // ...
}

class Apple extends Fruit {
    private String name = "apple";
    
    public Apple() {
        super();  // 顯式調用父構造器
        // 此處 name 已初始化為 "apple"
        tellName();  // 安全調用
        printName();
    }
    // ...
}

總結

該題考察構造器執行順序、多態提前生效、字段初始化時機三大核心機制交互。理解「父類構造器運行時子類字段尚未初始化,但多態已生效」是破解此類問題的關鍵。

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