簡體   English   中英

Java繼承:繼承超類成員和變量時的區別

[英]Java Inheritance: Difference while inheriting super class members and variables

我正在經歷Java繼承,並嘗試了以下代碼

class A {
    public int x = 1;

    public void print1() {
        System.out.println("Print from A");
    }
}

class B extends A {
    public int x = 2;

    public void print1() {
        System.out.println("Print from B");
    }
}

class C extends B {
    public int x = 3;

    public void print1() {
        System.out.println("Print from C");
    }

    public static void main(String aa[]) {
        C c = new C();
        ((A) c).print1();
        System.out.println(c.x);    
        System.out.println(((A) c).x);          
    }
}

輸出是

從C打印
3
1個

現在,我的問題是我可以從class C instance c訪問class A member x ,但是為什么我不能訪問class A method print1() 我知道,如果我願意,我可以叫new A().print1(); 但是,我想直接從C類的實例訪問它。 因此,通過覆蓋方法,頂部的父類方法是否在底部的子類中丟失了? 但是,父類成員將保留(盡管隱藏)。 為什么這樣? 有沒有一種方法可以在不創建A實例的情況下從C類中的A類調用方法?

編輯

這里發生了什么事?

當你說((A)c).print1(); ,JVM知道實際需要調用的實例是print1() ,其類型為C因此將執行類Cprint1()版本。

但是當您鍵入[ ((A)c).x ,您是在指狀態,而不是行為,也是在使用類型A引用。 因為編譯器知道什么是類型引用(在本例中為A ),所以在編譯時確定將使用x哪個版本。 這就是為什么您看到A的狀態。

Java doc中的以下語句可能對您也很有趣:

在類中,即使其類型不同,與超類中的字段具有相同名稱的字段也會隱藏超類的字段。 在子類中,無法通過其簡單名稱引用超類中的字段。 相反,必須通過super訪問該字段,這將在下一部分中介紹。 一般來說,我們不建議隱藏字段,因為這會使代碼難以閱讀。


為什么這樣呢?

回答您的問題:

問題1:

我的問題是我能夠從C類實例c訪問A類成員x

關於變量x ,因為它被聲明為public ,所以不能對其應用任何業務驗證。 而且,將其聲明為public的開發人員知道,封裝的概念現在不能應用(顯然不建議這樣做)。 因此,允許使用子實例訪問父狀態不會有任何危害。 因此,通過使用[ ((A)c).x ,您可以從類A訪問x

問題2:

但是,父類成員將保留(盡管隱藏)。 為什么這樣?

這是因為Java不允許您隨意使用super.super.super..來訪問繼承層次結構中任何父級的行為。 其背后的原因是保留封裝。 您只能使用super關鍵字訪問直接父類的行為,但不能超過此行為。

為了進一步解釋,假設您具有B類的實現:

class B extends A {
    public int x = 2;

    public void print1() {
        if(x >= 2) {
            System.out.println("Print from B");
        }
    }
}

現在,如果允許使用super.super ,則可以輕松地在類B print1()方法中繞過驗證,並可以從類C的實例調用A的print1()方法。

問題3:

但是,父類成員將保留(盡管隱藏)。 為什么這樣?

如前所述,保留封裝。

問題4:

有沒有一種方法可以在不創建A實例的情況下從C類中的A類調用方法?

因此,無法使用C的實例從A訪問print1()

當您覆蓋已經在base(parent)類中實現的函數時,在子類中調用該函數時,子類中的類將自動調用,因此在您情況下,當您調用C對象的print1()時:

C c = new C();
c.print1();

您將調用覆蓋功能。 但是,如果在程序的任何部分中您覺得在任何情況下都需要調用頂級父類( super無法實現),則應重新考慮您的設計策略; 也許您不應該覆蓋print1() ,甚至不應該將print1()首先放在基類中

在面向對象的編程中,只有方法才具有被覆蓋的能力。 您不能覆蓋數據成員。 這解釋了為什么獲得輸出。

A print1()方法在C被覆蓋,並且cC的對象。 因此,它將調用Cprint1()方法。 另一方面,變量x不被覆蓋。

要調用超類方法,您可以執行以下操作:

super.print1();

在這里,所有類都使用了同名實例變量來混淆編譯器,但是您應該知道,只有成員函數才能被覆蓋,而成員變量不能被覆蓋。

因此,所有三個x將分別存儲在內存中。 但是public void print1()將被其子類覆蓋。 所以,

  1. ((A)c).print1(); 這段代碼將調用C類中定義的重寫方法。無論在哪個類中強制轉換C類的Object都無關緊要。

但是在第二種情況下((A)c).x將打印A類的值。

  1. ((A)c).x :-這將引用變量x屬於Class A而不是Class C 因為變量從未在繼承中被覆蓋。

((A)c).print1(); 實際上,它類似於c.print1();。 不管您使用Class A進行類型轉換,它仍將調用Class C的print1()函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM