首页 > 代码库 > java继承会犯的小错误

java继承会犯的小错误

注意事项:阅读本文前应该先了解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     @Override11     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。