不能说的秘密-重载、重写与继承中的构造方法

我相信有些同学在学习过程中肯定会遇到一些问题,本帖就是针对同学门遇到的一些问题讲解一下。
首先,大家要先弄懂重载和重写的区别:
重载(Overloading):在同一个类中,方法名相同,参数列表不同,与返回值和修饰符无关。
重写(Overriding): 在子类与父类中,方法名和参数列表都相同,返回值类型要与父类中被重写的方法中声明的返回类型或子类型完全相同,修饰符的访问级别一定不能比被重写方法强,可以比被重写方法的弱 。
关于重写和重载的区别,这个帖子写的很详细

那么大家看一下这个问题,B类里面的run方法是重载了还是重写了A类中的run方法?:

    class  A{
        void run(String run){
            System.out.println("A");
        }
    }
    class B extends A{
        void run(int run){
            System.out.println("B");
        }
    }

由于新手很容易搞混淆重载和重写,一看在两个类里面就先入为主说是重写,但是仔细看一下,这里的A和B中的run方法参数列表是不同的,虽然在两个类里面,但是B类继承了A类,所以B类继承了A类里面的run方法(可以看成B类里面也有了一个run方法,跟A类里面的一模一样,只不过你看不到罢了),所以这也是方法的重载,不是重写!

那我们再来说一下继承中的构造方法:
首先我们先看一下这个例子:

    class A{
        public A(int a){
            System.out.print("A");
        }
    }
    class B extends A{
        public B(){
            System.out.print("B");
        }
    }
    class Test{
        public void main(String []args){
            B b=new B();//是输出 AB 还是会编译报错?若报错,在哪里报错?
        }
    }

在上面这个例子中,我们创建了一个B类的对象,但是你发现B的构造器会编译报错,为什么呢?我们首先来看一下继承中的构造方法要遵循哪些原则
1、子类的构造过程中必须调用其基类的构造方法(也就说在运行子类构造方法时肯定是先运行父类的构造方法)。
2、子类可以在自己的构造方法中使用super(argument_list)调用基类的构造方法。
  2.1、使用this(argument_list)调用本类的另外构造方法。
   2.2、如果调用super,必须写在子类构造方法的第一行。
3、如果子类的构造方法中没有显示的调用基类的构造方法,则系统默认调用基类的无参数构造方法。
4、如果子类构造方法中既没有显示调用基类构造方法,而基类又没有无参数的构造方法,则编译出错。

而在上面的例子中,我们创建了一个B类的对象,这时会先执行B的构造器,在执行B的构造器时会先执行其基类,也就是A的构造器。我们发现B类里面并没用super()调用基类的构造器,所以会默认调用它的无参构造,但是A类的构造器已经被A(int a)重载了,已经没有无参构造了,所以会在B的构造方法编译报错。违反了上述第4条。
我们再来验证一下上面几条规则:
1、

    class A{
        public A(){
            System.out.print("A");
        }
    }
    class B extends A{
        public B(){
            System.out.print("B");
        }
    }
    class Test{
        public void main(String []args){
            B b=new B();//会输出 AB
        }
    }

在new一个B的对象时会先执行它的构造方法,再执行它的构造方法时会先执行其基类的构造方法。
2、

    class A{
        public A(){
            System.out.print("A-无参");
        }
        public A(int a){
            System.out.print("A-有参");
        }
    }
    class B extends A{
        public B(){
        this(0);
            System.out.print("B-无参");
        }
        public B(int a){
            super(0);//必须写在子类构造器的首行,否则报错
            System.out.print("B-有参");
        }
    }
    class Test{
        public void main(String []args){
            B b=new B();//会输出 A-有参B-有参B-无参
        }
    }

在new B();时会先执行B的无参构造,而在B的无参构造器中调用了B的有参构造,所以会先执行A的有参构造,所以输出结果是A-有参B-有参B-无参。
3、

    class A{
        public A(){
            System.out.print("A-无参");
        }
        public A(int a){
            System.out.print("A-有参");
        }
    }
    class B extends A{
        public B(){
        this(0);
            System.out.print("B-无参");
        }
        public B(int a){
            System.out.print("B-有参");
        }
    }
    class Test{
        public void main(String []args){
            B b=new B();//会输出 A-无参B-有参B-无参
        }
    }

在new B();时会先执行B的无参构造,而在B的无参构造器并没有调用其基类的构造方法,所以会先默认执行A的无参构造,所以输出结果是A-无参B-有参B-无参。
4、看刚开始的例子。

猜你喜欢

转载自blog.csdn.net/qq_34598667/article/details/53066205