父类声明子类实例化以及重载,重写的一些问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/harryptter/article/details/85262294

最近面试碰到一道父类子类的面试题:

public class SuperClass {
    public int a;
    public SuperClass(){
        a=1;
        System.out.println("a is"+a);
    }
    public int getA(){
        return a;
    }
}
public class SubClass extends SuperClass {
     public int a =2;
     public SubClass(){
          System.out.println("a is"+a);
     }
     public int getA(){
          return a;
     }

     public static void main(String[] args) {
          SuperClass aClass = new SuperClass();
          SuperClass bClass = new SubClass();
          System.out.println("num1 is "+(aClass.a+bClass.a));
          System.out.println("num2 is "+(aClass.getA()+bClass.getA()));
          System.out.println("num3 is "+(aClass.a+bClass.getA()));
          System.out.println("num4 is "+(aClass.getA()+bClass.a));

     }
}

问输出的结果?

 这题一看就知道输出的四个结果肯定不同。

本身涉及到继承的知识,也就是重写的知识,不过这部分比较好理解,就是子类继承于父类,重写的父类的方法,那么调用的时候就调用子类自己的方法。

但是这道题恶心的地方在于bClass采用了父类声明子类实例化,那么bClass里面的参数到底是什么样的呢?

首先我们知道java继承中,通过继承,子类可以得到父类除构造函数以外所有的成员(包括成员变量和成员函数),但是要注意得到并不等于可以随便使用。子类能否使用(访问)父类的成员由父类成员的属性决定。

对于子类对象来说,在调用自身类的构造函数之前会先调用父类的构造函数。第一种情况:若子类构造函数向父类构造函数传递参数(通过super()在构造函数第一行实现),那么会自动调用父类有对应参数的构造函数;第二种情况:子类构造函数没有参数传递给父类构造函数,这种情况下系统会隐式调用父类无参数的构造函数,倘若父类没有任何构造函数,编译正常;若父类有带参数的构造函数而没有无参数的构造函数,编译时会发生错误。

对刚才的调用情况小结一下:无论如何,子类构造函数都会先调用父类构造函数,如果要传参数,通过super()实现;如果不传参数,那父类要么有一个无参数的构造函数,要么一个构造函数都没有,不然就会编译出错,通俗一点说就是我可以接受你啥也没有,但是不能有其他的但是没我要的

所以两个在二句实例化的时候,会先调用父类的构造方法。那么也就是说,结果中会先显示父类构造方法里面的打印数据。

由于bClass是通过父类声明子类实例化的方式定义的。

bClass这个实例是子类的,但是因为你声明时是用父类声明的,所以你用正常的办法访问不到子类自己的成员,只能访问到从父类继承来的成员。

在子类中重写父类中方法时,实例化父类调用该方法,执行时调用的是子类中重写的方法;

所以bClass.a=1,bClass.getA()返回2

所以最后的结果是:

我在代码中加入打印各项信息:

public class SuperClass {
    public int a;
    private int c;
    protected int d;
    public SuperClass(){
        a=1;
        c=5;
        System.out.println("a is"+a);
    }
    public int getA(){
        return a;
    }

    public int getC() {
        return c;
    }

    public int getD() {
        return d;
    }
}

public class SubClass extends SuperClass {
    public int a =2;
    private int c=6;
    protected int d =9;
    public SubClass(){
        System.out.println("a is"+a);
    }
    public int getA(){
        return a;
    }

    public int getC() {
        return c;
    }

    public int getD() {
        return d;
    }

    public static void main(String[] args) {
        SuperClass aClass = new SuperClass();
        SuperClass bClass = new SubClass();
        System.out.println("num1 is "+(aClass.a+bClass.a));
        System.out.println("num2 is "+(aClass.getA()+bClass.getA()));
        System.out.println("num3 is "+(aClass.a+bClass.getA()));
        System.out.println("num4 is "+(aClass.getA()+bClass.a));

        System.out.println("b.a is "+bClass.a);
        System.out.println("b.getA is "+bClass.getA());

        System.out.println("b.getC is "+bClass.getC());

        System.out.println("b.d is "+bClass.d);
        System.out.println("b.getD is "+bClass.getD());


        System.out.println("a.a is "+aClass.a);
        System.out.println("a.getA is "+aClass.getA());
        SubClass cClass = new SubClass();
        System.out.println("c.a is "+cClass.a);
        System.out.println("c.getA is "+cClass.getA());




    }
}

打印的结果:

 

 这里面会涉及到一个依赖倒置原则(DIP)

依赖倒置原则,DIP,Dependency Inverse Principle DIP的表述是: 
1、高层模块不应该依赖于低层模块,二者都应该依赖于抽象。 2、抽象不应该依赖于细节,细节应该依赖于抽象。 这里说的“依赖”是使用的意思,如果你调用了一个类的一个方法,就是依赖这个类,如果你直接调用这个类的方法,就是依赖细节,细节就是具体的类,但如果你调用的是它父类或者接口的方法,就是依赖抽象, 所以DIP说白了就是不要直接使用具体的子类,而是用它的父类的引用去调用子类的方法,这样就是依赖于抽象,不依赖具体。

其实简单的说,DIP的好处就是解除耦合,用了DIP之后,调用者就不知道被调用的代码是什么,因为调用者拿到的是父类的引用,它不知道具体指向哪个子类的实例,更不知道要调用的方法具体是什么,所以,被调用代码被偷偷换成另一个子类之后,调用者不需要做任何修改, 这就是解耦了。
 

猜你喜欢

转载自blog.csdn.net/harryptter/article/details/85262294