JAVA面试题解惑系列(三)——变量(属性)的覆盖

我们来看看这么一道题: 
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. }  
[java] view plaincopy
  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. }  


控制台的输出结果是多少呢?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. }  
[java] view plaincopy
  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. }  

这段代码的运行结果如下: 
  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. }  
[java] view plaincopy
  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. }  

运行结果: 
  • 子类变量

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. }  
[java] view plaincopy
  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. }  

运行结果: 
  • 子类变量

上面两段不同的代码,输出结果确是相同的。事实上,我们可以将父类和子类属性前的访问修饰符在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. }  
[java] view plaincopy
  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. }  

运行结果如下: 
  1. 子类静态变量
  2. 子类常量
  3. 子类静态常量

虽然上面的结果中包含“子类静态变量”和“子类静态常量”,但这并不表示父类的“静态变量”和“静态常量”可以被子类覆盖,因为它们都是属于类,而不属于对象。 

上面的例子中,我们一直用对象来对变量(属性)的覆盖做测试,如果是基本类型的变量,结果是否会相同呢?答案是肯定的,这里我们就不再一一举例说明了。 

最后,我们来做个总结。通过以上测试,可以得出一下结论: 
  1. 由于private变量受访问权限的限制,它不能被覆盖。
  2. 属性的值取父类还是子类并不取决于我们创建对象的类型,而是取决于我们定义的变量的类型。
  3. friendly、protected和public修饰符并不影响属性的覆盖。
  4. 静态变量和静态常量属于类,不属于对象,因此它们不能被覆盖。
  5. 常量可以被覆盖。
  6. 对于基本类型和对象,它们适用同样的覆盖规律。

我们再回到篇首的那道题,我想大家都已经知道答案了,输出结果应该是40。
转自:
作者:臧圩人(zangweiren) 
网址:http://zangweiren.javaeye.com
阅读更多
文章标签: java 面试题
个人分类: java面试题
上一篇JAVA面试题解惑系列(二)——到底创建了几个String对象?
下一篇JAVA面试题解惑系列(四)——final、finally和finalize的区别
想对作者说点什么? 我来说一句

JAVA面试题解惑系列合集

2018年01月07日 792KB 下载

JAVA面试题解惑系列

2009年05月19日 34KB 下载

JAVA面试题解惑系列合集.pdf

2010年05月20日 774KB 下载

Java面试题解惑系列

2011年09月21日 507KB 下载

JAVA面试题解惑系列.rar

2012年11月25日 1.16MB 下载

面试\JAVA,JEE基础,面试题汇总

2010年06月15日 478KB 下载

Java面试题(最全,最新)

2010年06月15日 1.47MB 下载

没有更多推荐了,返回首页

关闭
关闭