注意事项:阅读本文前应该先了解java的继承。本文定位为已经继承基础知识。
一:试图覆盖私有方法
先上代码
1 public class Father { 2 3 private void print() { 4 System.out.println("private print"); 5 } 6 7 public static void main(String[] args) { 8 Father father = new Son(); 9 father.print(); 10 } 11 12 }
1 public class Son extends Father { 2 3 public void print() { 4 System.out.println("public print"); 5 } 6 }
运行father的main方法,我们所期望的输出是public print,但是由于private方法默认为final方法,而且对导出子类是屏蔽的,在这种情况下,子类中的print就是子类中的一个新方法,并非是覆盖了父类的print方法,而且在子类的print方法上是无法加入@override注解的
结论:private方法无法被覆盖重写。
二:在一个构造器内部调用正在构造的对象的某个动态绑定方法
我们都知道,构造器的调用会从最高级的父类开始调用,然后不断反复递归下去。如果在父类的构造器中调用子类的构造方法输出变量,会发生什么?
1 public class Father { 2 3 public void draw() { 4 System.out.println("father draw"); 5 } 6 7 public Father() { 8 System.out.println("father:before draw"); 9 draw(); 10 System.out.println("father:after draw"); 11 } 12 13 }
1 public class Son extends Father { 2 3 private int number = 1; 4 5 public Son(int number) { 6 this.number = number; 7 System.out.println("init number:" + this.number); 8 } 9 10 @Override 11 public void draw() { 12 System.out.println("son draw number:" + this.number); 13 } 14 }
public class MainTest <T> extends Object{ public static void main(String[] args) { new Son(5); } }
控制台打印:
father:before draw
son draw number:0
father:after draw
init number:5
在测试中,我们new一个son同时传一个5进去,根据构造器的调用,我们知道在构造son的时候要先调用father的构造方法,而在father的构造器中,有调用draw方法。而在子类中,draw方法是将son中的number打印出来,number有一个默认的值为1,那在draw调用过程中,我们期望的输出应该是1,但实际输出的是0.
解决这一问题的关键所在是初始化的过程。在其他任何事物发生之前,分配给对象的储存空间初始化为二进制的零(你可以这样认为,number有一个默认值为1,但是在有这个默认值之前还有一个默认值为0),然后父类的构造器开始调用,再递归调用子类的构造方法,然后按照声明的顺序初始化成员变量。而在这个初始化步骤之前,一切变量都是0,或者null。
三:调用子类覆盖的父类静态方法。
1 public class Father { 2 3 public static String staticGet() { 4 return "father static get"; 5 } 6 7 public String dynaminGet() { 8 return "father dynamin get"; 9 } 10 }
1 public class Son extends Father { 2 3 public static String staticGet() { 4 return "son static get"; 5 } 6 7 public String dynaminGet() { 8 return "son dynamin get"; 9 } 10 }
public class MainTest <T> extends Object{ public static void main(String[] args) { Father f = new Son(); System.out.println(f.staticGet()); System.out.println(f.dynaminGet()); } }
控制台输出:
father static get
son dynamin get
我们期望的输出应该都是son的方法而不是father的方法,但是调用静态方法的时候是father而不是son。这是因为如果某个方法是静态的,那么它的行为就不具有多态性,静态方法是与类绑定的,而不是与单个对象相关联的。子类并不能覆盖父类的静态方法,事实上,在son中的staticGet方法上无法添加@override注解。
四:子类的域可以覆盖父类的域
1 public class Father { 2 3 public int number = 3; 4 5 public int getNumber() { 6 return number; 7 } 8 }
1 public class Son extends Father { 2 3 public int number = 2; 4 5 public int getNumber() { 6 return number; 7 } 8 9 public int getSuperNumber() { 10 return super.number; 11 } 12 }
public class MainTest <T> extends Object{ public static void main(String[] args) { Son s = new Son(); System.out.println(s.getNumber()); System.out.println(s.getSuperNumber()); } }
控制台输出:
2
3
如果son中的number覆盖掉father的number,那么输出的两个number都应该是2,事实上从son中得到father的number为3,这说明son中的number并没有覆盖掉father中的number。在本例中,son.number和father.number分配到了不同的储存空间。换句话来说,在son中其实包含了两个名为number的变量:它自己的和从father中继承得到的。然而在son中的number所产生的默认域并非father中的number域。因此,为了得到father中的number,必须显式地指明super.field。