Java编程思想: 多态

再论向上转型

由于继承的存在, 我们可以将子类的对象引用当做基类的一个对象, 即将子类对象向上转型为基类对象.

class A {
  public void show() {
    System.out.println("A show()");
  }
}
class B extends A{
  public void show() {
    System.out.println("B show()");
  }
}
public class C {
  public static void func(A a) {
    a.show();
  }
  public static void main(String[] args) {
    B b = new B();
    // B show()
    func(b);
  }
}

 

动态绑定

绑定

将一个方法调用同一个方法主体关联起来被称作绑定, 在程序执行前进行绑定称为前期绑定.

当我们编写上例中的代码:

public static void func(Cycle c) {
  c.ride();
}

我们如何确定方法ride所对应的对象c是什么类型?

这就引发了动态绑定的概念. 即在静态编译阶段, 我们实际上并不知道c的类型是什么. 只有等到运行阶段, 我们通过某种方法, 获得此方法的类签名, 才能正确的调用此方法.

覆盖引发动态绑定

考虑上例中: 只有子类覆盖了基类的方法, 才会引发多态. 在动态运行阶段, a.show()被执行时候, 其方法签名若为B对象, 则它事先查看A中是否有show方法, 如果有则进行向下转型, 使用实际的B对象调用show.

如果使用private/final阻止多态的产生, 那程序则通常不会按我们预期的进行执行:

class A {
  private void f() {
    System.out.println("A f()");
  }
  public static void main(String[] args) {
    A a = new B();
    // A f()
    a.f();
  }
}
public class B extends A{
//  @Override
  public void f() {
    System.out.println("B f()");
  }
}

如果我们要强制执行多态, 则最好使用@Override进行强制覆盖.

 

构造器和多态

构造器的调用顺序

构造器的调用顺序必然是先调用基类构造器, 然后递归下去调用子类构造器. 

构造器的主要任务是: 检查对象是否被正确构造. 所以只有父类构造器正确的调用, 才可确保子类的构造器正确的调用.

继承与清理

在继承情况下, 如果需要手动编写清理函数, 那么子类的清理函数必须调用父类的清理函数.

对象的销毁顺序应该和初始化顺序相反. 考虑顺序定义两个对象A,B, 那么在B中是可以引用对象A的. 正确的销毁步骤是调用B的清理函数, 然后在调用A的清理函数.

如果是先调用A的清理函数, 那么在调用B的任何函数时(包括销毁函数), 在B中的A对象是无效的, 则导致异常的发生.

class A {
  public void dispose() {
    System.out.println("A dispose");
  }
}
public class B extends A{
  private A a = new A();
  public void dispose() {
    System.out.println("B dispose.");
    a.dispose();
    super.dispose();
  }
  public static void main(String[] args) {
    B b = new B();
    b.dispose();
  }
}

存在一种特殊的情况, 即一个对象被多个对象所共享. 那么需要通过计数来判断什么时候需要清理对象:

class Shared {
  private int refcount = 0;
  private static long counter = 0;
  private final long id = counter++;
  public Shared() {
    System.out.println("Creating " + this);
  }
  public void addRef() {
    refcount++;
  }
  public void dispose() {
    if (--refcount == 0) {
      System.out.println("disposing " + this);
    }
  }
  public String toString() {
    return "Shared " + id;
  }
}

class Composing {
  private Shared shared;
  private static long counter = 0;
  private final long id = counter++;
  public Composing(Shared shared) {
    System.out.println("Creating " + this);
    this.shared = shared;
    this.shared.addRef();
  }

  protected void dispose() {
    System.out.println("disposing " + this);
    shared.dispose();
  }
  public String toString() {
    return "Composing " + id;
  }
}

public class ReferenceCounting {
  public static void main(String[] args) {
    Shared shared = new Shared();
    Composing[] composing = {
        new Composing(shared),
        new Composing(shared),
        new Composing(shared),
        new Composing(shared),
        new Composing(shared)
    };
    for (Composing c: composing) {
      c.dispose();
    }
  }
}

构造器内部的多态方法的行为

假设class A extends B, 那么在A中使用super来引用B. 但super的类型实际上是A的, 它向上转型为B, 通过强制类型转换(B) super.

这可以解释构造器中的多态行为:

class A {
  A() {
    System.out.println("A constructor.");
    show();
  }
  public void show() {
    System.out.println("A show");
  }
}
public class B extends A{
  B() {
    System.out.println("B constructor.");
  }
  public void show() {
    System.out.println("B show");
  }
  public static void main(String[] args) {
    // A constructor.
//    B show
//    B constructor.
    B b = new B();
  }
}

 

转载于:https://my.oschina.net/voler/blog/715795

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值