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     @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。

原文地址:https://www.cnblogs.com/night-wind/p/3903268.html