天天看點

JAVA面試題解惑系列(三)——變量(屬性)的覆寫

我們來看看這麼一道題:

Java代碼

class ParentClass {   

    public int i = 10;   

}   

public class SubClass extends ParentClass {   

    public int i = 30;   

    public static void main(String[] args) {   

        ParentClass parentClass = new SubClass();   

        SubClass subClass = new SubClass();   

        System.out.println(parentClass.i + subClass.i);   

    }   

}  

控制台的輸出結果是多少呢?20?40?還是60?

變量,或者叫做類的屬性,在繼承的情況下,如果父類和子類存在同名的變量會出現什麼情況呢?這就是這道題要考查的知識點——變量(屬性)的覆寫。

這個問題雖然簡單,但是情況卻比較複雜。因為我們不僅要考慮變量、靜态變量和常量三種情況,還要考慮private、friendly(即不加通路修飾符)、protected和public四種通路權限下對屬性的不同影響。

我們先從普通變量說起。依照我們的慣例,先來看一段代碼:

    private String privateField = "父類變量--private";   

    /* friendly */String friendlyField = "父類變量--friendly";   

    protected String protectedField = "父類變量--protected";   

    public String publicField = "父類變量--public";   

    // private的變量無法直接通路,是以我們給他增加了一個通路方法   

    public String getPrivateFieldValue() {   

        return privateField;   

    private String privateField = "子類變量--private";   

    /* friendly */String friendlyField = "子類變量--friendly";   

    protected String protectedField = "子類變量--protected";   

    public String publicField = "子類變量--public";   

        // 為了便于查閱,我們統一按照private、friendly、protected、public的順序   

        // 輸出下列三種情況中變量的值   

        // ParentClass類型,ParentClass對象   

        ParentClass parentClass = new ParentClass();   

        System.out.println("ParentClass parentClass = new ParentClass();");   

        System.out.println(parentClass.getPrivateFieldValue());   

        System.out.println(parentClass.friendlyField);   

        System.out.println(parentClass.protectedField);   

        System.out.println(parentClass.publicField);   

        System.out.println();   

        // ParentClass類型,SubClass對象   

        ParentClass subClass = new SubClass();   

        System.out.println("ParentClass subClass = new SubClass();");   

        System.out.println(subClass.getPrivateFieldValue());   

        System.out.println(subClass.friendlyField);   

        System.out.println(subClass.protectedField);   

        System.out.println(subClass.publicField);   

        // SubClass類型,SubClass對象   

        SubClass subClazz = new SubClass();   

        System.out.println("SubClass subClazz = new SubClass();");   

        System.out.println(subClazz.getPrivateFieldValue());   

        System.out.println(subClazz.friendlyField);   

        System.out.println(subClazz.protectedField);   

        System.out.println(subClazz.publicField);   

這段代碼的運作結果如下:

ParentClass parentClass = new ParentClass();

父類變量--private

父類變量--friendly

父類變量--protected

父類變量--public

ParentClass subClass = new SubClass();

子類變量--private

SubClass subClazz = new SubClass();

子類變量--friendly

子類變量--protected

子類變量--public

從上面的結果中可以看出,private的變量與其它三種通路權限變量的不同,這是由于方法的重寫(override)而引起的。關于重寫知識的回顧留給以後的章節,這裡我們來看一下其它三種通路權限下變量的覆寫情況。

分析上面的輸出結果就會發現,變量的值取決于我們定義的變量的類型,而不是建立的對象的類型。

在上面的例子中,同名的變量通路權限也是相同的,那麼對于名稱相同但是通路權限不同的變量,情況又會怎樣呢?事實勝于雄辯,我們繼續來做測試。由于private變量的特殊性,在接下來的實驗中我們都把它排除在外,不予考慮。

由于上面的例子已經說明了,當變量類型是父類(ParentClass)時,不管我們建立的對象是父類(ParentClass)的還是子類(SubClass)的,都不存在屬性覆寫的問題,是以接下來我們也隻考慮變量類型和建立對象都是子類(SubClass)的情況。

    /* friendly */String field = "父類變量";   

    protected String field = "子類變量";   

        System.out.println(subClass.field);   

運作結果:

子類變量

    public String field = "父類變量";   

上面兩段不同的代碼,輸出結果确是相同的。事實上,我們可以将父類和子類屬性前的通路修飾符在friendly、protected和public之間任意切換,得到的結果都是相同的。也就是說通路修飾符并不影響屬性的覆寫,關于這一點大家可以自行編寫測試代碼驗證。

對于靜态變量和常量又會怎樣呢?我們繼續來看:

    public static String staticField = "父類靜态變量";   

    public final String finalField = "父類常量";   

    public static final String staticFinalField = "父類靜态常量";   

    public static String staticField = "子類靜态變量";   

    public final String finalField = "子類常量";   

    public static final String staticFinalField = "子類靜态常量";   

        System.out.println(SubClass.staticField);   

        // 注意,這裡的subClass變量,不是SubClass類   

        System.out.println(subClass.finalField);   

        System.out.println(SubClass.staticFinalField);   

運作結果如下:

子類靜态變量

子類常量

子類靜态常量

雖然上面的結果中包含“子類靜态變量”和“子類靜态常量”,但這并不表示父類的“靜态變量”和“靜态常量”可以被子類覆寫,因為它們都是屬于類,而不屬于對象。

上面的例子中,我們一直用對象來對變量(屬性)的覆寫做測試,如果是基本類型的變量,結果是否會相同呢?答案是肯定的,這裡我們就不再一一舉例說明了。

最後,我們來做個總結。通過以上測試,可以得出一下結論:

由于private變量受通路權限的限制,它不能被覆寫。

屬性的值取父類還是子類并不取決于我們建立對象的類型,而是取決于我們定義的變量的類型。

friendly、protected和public修飾符并不影響屬性的覆寫。

靜态變量和靜态常量屬于類,不屬于對象,是以它們不能被覆寫。

常量可以被覆寫。

對于基本類型和對象,它們适用同樣的覆寫規律。

我們再回到篇首的那道題,我想大家都已經知道答案了,輸出結果應該是40。