多态

多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。多态不但能够改变代码的组织结构和可读性,还能够创建可扩展的程序,即无论在项目最初创建时还是在需要添加新功能时都可以“生长”的程序。

向上转型

传统的类继承图的绘制方法是将根置于页面的顶端,然后逐渐向下。由导出类转型成基类,在继承图上是向上移动的,所以这种将Wind引用转换为Instrument引用的动作,一般称为向上转型

class Instrument {}
public class Wind extends Instrument {
    public static void main(String[] args) {
        Instrument i = new Wind();
    }
}

方法调用绑定

将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现),叫做前期绑定。若在运行时根据对象的类型进行绑定,叫做后期绑定,也叫动态绑定运行时绑定
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都属于后期绑定。这意味着通常情况下,我们不必判断是否应该进行后期绑定,他会自动发生。
在“几何形状”的例子中,有一个基类Shape,以及多个导出类:Circle、Square、Triangle。下面的继承图展示他们之间的关系:

向上转型很简单:
Shape s = new Circle();
这里,创建了一个Circle对象,并把得到的引用立即赋值给Shape,通过继承,Circle就是一种Shape。因此,编译器认可这条语句,不会报错。假设调用一个基类方法(它已在导出类中被覆盖):
s.draw();
由于后期绑定(多态),编译器正确的调用了Circle.draw()方法。

缺陷:“覆盖”私有方法

public class PrivateOverride {
    private void f() {
        System.out.println("private f()");
    }
    public static void main(String[] args){
        PrivateOverride po = new Derived();
        po.f();
    }
}

class Derived extends PrivateOverride {
    public void f() {
        System.out.println("public f()");
    }
}/*Output:
Private f()*/

我们所期望的输出是public f(),但是由于private方法被自动认为是final方法,而且对导出类是屏蔽的。因此,在这种情况下,Derived类中的f()方法就是一个全新的方法。基类中的f()方法在子类Derived中不可见,因此不能被重载。

缺陷:域与静态方法

只有普通的方法调用才可以是多态的,如果直接访问某个域,这个访问就会在编译期进行解析,如下:

class Super {
    public int field = 0;
    public int getFiled() { return field; }
}

class Sub extends Super {
    public int field = 1;
    public int getField() { return field; }
    public int getSuperField() { return super.field; }
}

public class FieldAccess {
    public static void main(String[] args){
        Super sup = new Sub();
        System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getFiled());
        Sub sub = new Sub();
        System.out.println("sub.field = " + sub.field + ", sub.getField() = " + sub.getFiled() + ", sub.getSuperField() = " + sub.getSuperField());
    }
}/*Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0*/

当Sub对象转型为Super引用时,任何域访问操作都将由编译器解析,因此不是多态的。本例中,为Super.field和Sub.field分配了不同的存储空间,实际上Sub包含2个称为field的域:他自己的和从Super得到的。然而在引用Sub的field时产生的默认域并非Super版本的field域,因此为了得到Super.field就必须显示的指明super.field。
如果某个方法是静态的,他的行为就不具有多态性:

class StaticSuper {
    public static String staticGet() {
        return "Base staticGet()";
    }
    public String dynamicGet() {
        return "Base dynamicGet()";
    }
}

class StaticSub extends StaticSuper {
    public static String staticGet() {
        return "Deriver staticGet()";
    }
    public String dynamicGet() {
        return "Deriver dynamicGet()";
    }
}

public class StaticPolymorphism {
    public static void main(String[] args){
        StaticSuper sup = new StaticSub();
        System.out.println(sup.staticGet());
        System.out.println(sup.dynamicGet());
    }
}/*Output:
Base staticGet()
Deriver dynamicGet()*/

静态方法是与类,而并非与单个对象相关联的。

构造器与多态

构造器的调用顺序:
1.调用基类构造器。这个步骤会不断的反复递归下去,首先是构造层次结构的根,然后是下一层导出类,直到最底层的导出类。
2.按声明顺序调用成员的初始化方法。
3.调用导出类的构造器主体。
初始化的实际过程:
1.在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
2.调用基类构造器。
3.按照声明的顺序调用成员的初始化方法。
3.调用导出类的构造器主体。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值