zoukankan      html  css  js  c++  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     @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。

  • 相关阅读:
    Load Balancing 折半枚举大法好啊
    Big String 块状数组(或者说平方分割)
    K-th Number 线段树(归并树)+二分查找
    D. Powerful array 莫队算法或者说块状数组 其实都是有点优化的暴力
    CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。
    运行时Runtime的API
    UIView的API
    UIControl的API
    UIScrollView的API
    使用KVO键值监听
  • 原文地址:https://www.cnblogs.com/night-wind/p/3903268.html
Copyright © 2011-2022 走看看