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

來源:互聯網
上載者:User
 
JAVA面試題解惑系列(三)——變數(屬性)的覆蓋

關鍵字: java 面試題 繼承 變數的覆蓋 屬性

作者:臧圩人(zangweiren)

網址:http://zangweiren.javaeye.com

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

我們來看看這麼一道題:
Java代碼

  1. class ParentClass {  
  2.     public int i = 10;  
  3. }  
  4.   
  5. public class SubClass extends ParentClass {  
  6.     public int i = 30;  
  7.   
  8.     public static void main(String[] args) {  
  9.         ParentClass parentClass = new SubClass();  
  10.         SubClass subClass = new SubClass();  
  11.         System.out.println(parentClass.i + subClass.i);  
  12.     }  
  13. }  
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代碼

  1. class ParentClass {  
  2.     private String privateField = "父類變數--private";  
  3.   
  4.     /* friendly */String friendlyField = "父類變數--friendly";  
  5.   
  6.     protected String protectedField = "父類變數--protected";  
  7.   
  8.     public String publicField = "父類變數--public";  
  9.   
  10.     // private的變數無法直接存取,因此我們給他增加了一個存取方法  
  11.     public String getPrivateFieldValue() {  
  12.         return privateField;  
  13.     }  
  14. }  
  15.   
  16. public class SubClass extends ParentClass {  
  17.     private String privateField = "子類變數--private";  
  18.   
  19.     /* friendly */String friendlyField = "子類變數--friendly";  
  20.   
  21.     protected String protectedField = "子類變數--protected";  
  22.   
  23.     public String publicField = "子類變數--public";  
  24.   
  25.     // private的變數無法直接存取,因此我們給他增加了一個存取方法  
  26.     public String getPrivateFieldValue() {  
  27.         return privateField;  
  28.     }  
  29.   
  30.     public static void main(String[] args) {  
  31.         // 為了便於查閱,我們統一按照private、friendly、protected、public的順序  
  32.         // 輸出下列三種情況中變數的值  
  33.   
  34.         // ParentClass類型,ParentClass對象  
  35.         ParentClass parentClass = new ParentClass();  
  36.         System.out.println("ParentClass parentClass = new ParentClass();");  
  37.         System.out.println(parentClass.getPrivateFieldValue());  
  38.         System.out.println(parentClass.friendlyField);  
  39.         System.out.println(parentClass.protectedField);  
  40.         System.out.println(parentClass.publicField);  
  41.   
  42.         System.out.println();  
  43.   
  44.         // ParentClass類型,SubClass對象  
  45.         ParentClass subClass = new SubClass();  
  46.         System.out.println("ParentClass subClass = new SubClass();");  
  47.         System.out.println(subClass.getPrivateFieldValue());  
  48.         System.out.println(subClass.friendlyField);  
  49.         System.out.println(subClass.protectedField);  
  50.         System.out.println(subClass.publicField);  
  51.   
  52.         System.out.println();  
  53.   
  54.         // SubClass類型,SubClass對象  
  55.         SubClass subClazz = new SubClass();  
  56.         System.out.println("SubClass subClazz = new SubClass();");  
  57.         System.out.println(subClazz.getPrivateFieldValue());  
  58.         System.out.println(subClazz.friendlyField);  
  59.         System.out.println(subClazz.protectedField);  
  60.         System.out.println(subClazz.publicField);  
  61.     }  
  62. }  
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. ParentClass subClass = new SubClass();
  7. 子類變數--private
  8. 父類變數--friendly
  9. 父類變數--protected
  10. 父類變數--public
  11. SubClass subClazz = new SubClass();
  12. 子類變數--private
  13. 子類變數--friendly
  14. 子類變數--protected
  15. 子類變數--public

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

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

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

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

Java代碼
  1. class ParentClass {  
  2.     /* friendly */String field = "父類變數";  
  3. }  
  4.   
  5. public class SubClass extends ParentClass {  
  6.     protected String field = "子類變數";  
  7.   
  8.     public static void main(String[] args) {  
  9.         SubClass subClass = new SubClass();  
  10.         System.out.println(subClass.field);  
  11.     }  
  12. }  
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);}}

運行結果:

  • 子類變數

Java代碼

  1. class ParentClass {  
  2.     public String field = "父類變數";  
  3. }  
  4.   
  5. public class SubClass extends ParentClass {  
  6.     protected String field = "子類變數";  
  7.   
  8.     public static void main(String[] args) {  
  9.         SubClass subClass = new SubClass();  
  10.         System.out.println(subClass.field);  
  11.     }  
  12. }  
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);}}

運行結果:

  • 子類變數

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

對於靜態變數和常量又會怎樣呢?我們繼續來看:
Java代碼

  1. class ParentClass {  
  2.     public static String staticField = "父類靜態變數";  
  3.   
  4.     public final String finalField = "父類常量";  
  5.   
  6.     public static final String staticFinalField = "父類靜態常量";  
  7. }  
  8.   
  9. public class SubClass extends ParentClass {  
  10.     public static String staticField = "子類靜態變數";  
  11.   
  12.     public final String finalField = "子類常量";  
  13.   
  14.     public static final String staticFinalField = "子類靜態常量";  
  15.   
  16.     public static void main(String[] args) {  
  17.         SubClass subClass = new SubClass();  
  18.         System.out.println(SubClass.staticField);  
  19.         // 注意,這裡的subClass變數,不是SubClass類  
  20.         System.out.println(subClass.finalField);  
  21.         System.out.println(SubClass.staticFinalField);  
  22.     }  
  23. }  
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);// 注意,這裡的subClass變數,不是SubClass類System.out.println(subClass.finalField);System.out.println(SubClass.staticFinalField);}}

運行結果如下:

  1. 子類靜態變數
  2. 子類常量
  3. 子類靜態常量

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

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

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

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

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.