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

作者:臧圩人(zangweiren)
網址:http://zangweiren.javaeye.com

>>>轉載請註明出處!<<<

我們來看看這麼一道題:
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四種訪問權限下對屬性的不同影響。

我們先從普通變量說起。依照我們的慣例,先來看一段代碼:
Java代碼
class ParentClass {   
    private String privateField = "父類變量--private";   
  
    /* friendly */String friendlyField = "父類變量--friendly";   
  
    protected String protectedField = "父類變量--protected";   
  
    public String publicField = "父類變量--public";   
  
    // private的變量無法直接訪問,因此我們給他增加了一個訪問方法   
    public String getPrivateFieldValue() {   
        return privateField;   
    }   
}   
  
public class SubClass extends ParentClass {   
    private String privateField = "子類變量--private";   
  
    /* friendly */String friendlyField = "子類變量--friendly";   
  
    protected String protectedField = "子類變量--protected";   
  
    public String publicField = "子類變量--public";   
  
    // private的變量無法直接訪問,因此我們給他增加了一個訪問方法   
    public String getPrivateFieldValue() {   
        return privateField;   
    }   
  
    public static void main(String[] args) {   
        // 爲了便於查閱,我們統一按照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);   
  
        System.out.println();   
  
        // 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);   
    }   
}  

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

1、ParentClass parentClass = new ParentClass();
2、父類變量--private
3、父類變量--friendly
4、父類變量--protected
5、父類變量--public
6、
7、ParentClass subClass = new SubClass();
8、子類變量--private
9、父類變量--friendly
10、父類變量--protected
11、父類變量--public
12、
13、SubClass subClazz = new SubClass();
14、子類變量--private
15、子類變量--friendly
16、子類變量--protected
17、子類變量--public

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

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

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

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

Java代碼
class ParentClass {   
    /* friendly */String field = "父類變量";   
}   
  
public class SubClass extends ParentClass {   
    protected String field = "子類變量";   
  
    public static void main(String[] args) {   
        SubClass subClass = new SubClass();   
        System.out.println(subClass.field);   
    }   
}  

運行結果:

1、子類變量

Java代碼
class ParentClass {   
    public String field = "父類變量";   
}   
  
public class SubClass extends ParentClass {   
    protected String field = "子類變量";   
  
    public static void main(String[] args) {   
        SubClass subClass = new SubClass();   
        System.out.println(subClass.field);   
    }   
}  

運行結果:

1、子類變量

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

對於靜態變量和常量又會怎樣呢?我們繼續來看:
Java代碼
class ParentClass {   
    public static String staticField = "父類靜態變量";   
  
    public final String finalField = "父類常量";   
  
    public static final String staticFinalField = "父類靜態常量";   
}   
  
public class SubClass extends ParentClass {   
    public static String staticField = "子類靜態變量";   
  
    public final String finalField = "子類常量";   
  
    public static final String staticFinalField = "子類靜態常量";   
  
    public static void main(String[] args) {   
        SubClass subClass = new SubClass();   
        System.out.println(SubClass.staticField);   
        System.out.println(subClass.finalField);   
        System.out.println(SubClass.staticFinalField);   
    }   
}  

運行結果如下:

1、子類靜態變量
2、子類常量
3、子類靜態常量

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

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

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

1、由於private變量受訪問權限的限制,它不能被覆蓋。
2、屬性的值取父類還是子類並不取決於我們創建對象的類型,而是取決於我們定義的變量的類型。
3、friendly、protected和public修飾符並不影響屬性的覆蓋。
4、靜態變量和靜態常量屬於類,不屬於對象,因此它們不能被覆蓋。
5、常量可以被覆蓋。
6、對於基本類型和對象,它們適用同樣的覆蓋規律。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章