Java面试题30——面向对象特征之多态

前提是你 提交于 2020-01-24 09:46:08

面向对象特征——多态

多态有两种描述方式,一种是方法的多态性、一种是对象的多态性。多态是发生在编译期间的。

一、方法的多态性:

① 方法的重载:同一个方法名称,会根据传入参数的类型及个数不同执行不同的方法体;

② 方法的覆写: 同一个方法,会根据子类的不同,实现不同的功能。也就是继承了父类的多个子类对父类方法的重写,导致了方法的多态性。

二、对象的多态性(指的是发生在继承关系之中,子类和父类之间转换问题)

父类对象——子类实例:

① 向上转型(自动完成):父类 父类对象 = 子类实例 <new 实现>

子类对象——父类实例:

② 向下转型(强制完成):子类 子类对象 = (子类)父类实例 <new 实现>

下面看一个实例,让我们加深对多态的理解!!

class A {
    public void m(A a) {
        System.out.println("AA");
    }
    public void m(D d) {
        System.out.println("AD");
    }
}
class B extends A {
    @Override
    public void m(A a) {
        System.out.println("BA");
    }
    public void m(B b) {
        System.out.println("BD");
    }
    public static void main(String[] args) {
        A a = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        a.m(a);
        a.m(b);
        a.m(c);
        a.m(d);
    }
}
class C extends B{}
class D extends B{}
答:打印结果如下。

BA
BA
BA
AD
题目解析:
这道题很好的体现了方法的多态性和对象的多态性。父类对象指向子类实例,体现对象的多态性,而方法的参数不同,体现了方法的多态性。
第一个 BA:因为 A 的 m() 方法,被子类 B 重写了,所以输出是:BA;
第二个 BA:因为 B 是 A 的子类,当调用父类 m() 方法时,发现 m() 方法被 B 类重写了,所以会调用 B 中的 m() 方法,输出就是:BA;
第三个 BA:因为 C 是 B 的子类,会直接调用 B 的 m() 方法,所以输出就是:BA;
第四个 AD:因为 D 是 A 的子类,所以会调用 A 的 m() 方法,所以输出就是:AD。

我们看到,我们并不需要重新改动代码,只需要实现继承关系,就可以很方便的调用各个类的不同方法。

上面我们提到子类可以重写父类的方法,那子类可以重写父类的成员变量吗?

class A {
    public int x = 0;
    public static int y = 0;
    public void m() {
        System.out.print("A");
    }
}
class B extends A {
    public int x = 1;
    public static int y = 2;
    public void m() {
        System.out.print("B");
    }
    public static void main(String[] args) {
        A myClass = new B();
        System.out.print(myClass.x);
        System.out.print(myClass.y);
        myClass.m();
    }
}

上面的例子的输出值是什么呢?一个指向子类实例的父类应用,访问到的x和y到底是什么呢?输出的值为00B,这是因为子类不可以重写父类的成员变量。我们访问到的父类的变量没有发生改变。在子类中定义的变量只是子类的变量,父类不能访问,只能用子类的引用去访问。这是为什么呢?

因为在一个类中,子类中的成员变量如果和父类中的成员变量同名,那么即使他们类型不一样,只要名字一样。父类中的成员变量都会被隐藏。在子类中,父类的成员变量不能被简单的用引用来访问。而是,必须从父类的引用获得父类被隐藏的成员变量,一般来说,我们不推荐隐藏成员变量,因为这样会使代码变得难以阅读。

记录一下子类父类的加载

class ExecTest {
    public static void main(String[] args) {
        Son son = new Son();
    }
}
class Parent{
    {
        System.out.print("1");
    }
    static{
        System.out.print("2");
    }
    public Parent(){
        System.out.print("3");
    }
}
class Son extends Parent{
    {
        System.out.print("4");
    }
    static{
        System.out.print("5");
    }
    public Son(){
        System.out.print("6");
    }
}

打印的结果如下:

251346

加载顺序如下:

  • 执行父类的静态成员;2
  • 执行子类的静态成员;5
  • 父类的实例成员和实例初始化;1
  • 执行父类构造方法;3
  • 子类的实例成员和实例初始化;4
  • 子类构造方法。6
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!