我們來看看這麼一道題:
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。