Thinking in Java:第七章 复用类

     复用代码是java众多引人注目的功能之一。本章介绍两种达到这一目的的方法:

     组合:只需在新类中产生现有类的对象。

     继承:按照现有类的类型来创建新类,无需改变现有类的形式,此阿勇现有类的形式并在其中添加新代码。

7.1 组合语法

       如果想要初始化类中的对象引用,可以在代码中的如下位置进行:

       1、在定义对象的地方。这意味着它们总能在构造器被调用之前初始化。

       2、在类的构造器中。

       3、就在正在使用这些对象之前,这种方式成为惰性初始化。

       4、使用实例初始化。

7.2 继承语法

      当创建了一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你在使用基类直接创建的对象是一样的。而这的区别在于,后者来自于外部,而基类的子对象被包装在导出类的对象内部。

        对基类子对象的正确初始化是至关重要的,而且也仅有一种方法来保证这一点:在构造器中调用积累构造器来执行初始化,而积累构造器具有执行基类初始化所需要的所有知识和能力。java会自动在导出类的构造器中插入对基类默认构造器的调用。

       但是如果没有默认的构造器,或者想调用一个带参数的基类构造器,就必须使用关键字super显示地编写基类构造器的语句,并且配以适当的参数列表。而且调用积累构造器是你在导出类构造器中要做的第一件事。

7.3 代理

       第三种关系成为代理,java并没有提供直接的支持。这是继承与组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该对象成员的所有方法(就像集成)。

       其例子如下:

//: reusing/SpaceShip.java

public class SpaceShip extends SpaceShipControls {
  private String name;
  public SpaceShip(String name) { this.name = name; }
  public String toString() { return name; }
  public static void main(String[] args) {
    SpaceShip protector = new SpaceShip("NSEA Protector");
    protector.forward(100);
  }
} ///:~

//: reusing/SpaceShipDelegation.java

public class SpaceShipDelegation {
  private String name;
  private SpaceShipControls controls =
    new SpaceShipControls();
  public SpaceShipDelegation(String name) {
    this.name = name;
  }
  // Delegated methods:
  public void back(int velocity) {
    controls.back(velocity);
  }
  public void down(int velocity) {
    controls.down(velocity);
  }
  public void forward(int velocity) {
    controls.forward(velocity);
  }
  public void left(int velocity) {
    controls.left(velocity);
  }
  public void right(int velocity) {
    controls.right(velocity);
  }
  public void turboBoost() {
    controls.turboBoost();
  }
  public void up(int velocity) {
    controls.up(velocity);
  }
  public static void main(String[] args) {
    SpaceShipDelegation protector =
      new SpaceShipDelegation("NSEA Protector");
    protector.forward(100);
  }
} ///:~

7.4 结合使用组合和集成

      虽然编译器强制你去初始化基类,并且要求你要在构造器起始处就要这么做,但是它并不监督你并须将成员对象也初始化,因此在这一点上你自己必须时刻注意。

      有时候需要在类的生命周期内执行一些必要的清理活动。这可以学习c++的析构函数,我们在每个继承对象中添加一个dispose方法,需要注意对基类清理方法和成员对象清理方法的调用顺序,以防某个子对象依赖于另一个子对象情形的方法。一般而言,所采用的形式应该与C++编译器在其析构函数上所施加的形式相同:首先,执行类的所有特定的清理动作,其顺序同生成顺序相反(通常这就要求基类元素仍旧存活);然后,就如我们所示范的那样,调用基类的清理方法。

       许多情况下,清理并不是问题,仅需要让垃圾回收器完成该动作就行。但是,一旦涉及垃圾回收,能够信赖的事情就不多了,因为垃圾回收器可能以任何它想要的顺序来回收对象。最好的办法是,除了内存以外,不能依赖垃圾回收器去做任何事情。如果需要进行清理,最好是编写你自己的清理方法,而不是使用finalize。

       如果java的基类拥有某个已被对此重载的方法名称,那么在导出类中重新定义的方法名称不会屏蔽其在基类中的任何版本(这一点与c++不同)。

       java se5中新加了override注解,它可以防止你在不想重载时而意外进行了重载。

7.5 在组合和继承之间选择

      到底是使用组合还是继承,一个最清晰的判断方法是问一问你自己是否需要从新类向基类进行向上转型。

7.6 protected关键字

       protected关键字,指明就类用户而言,它是一个private的,但对于任何继承于此类的导出类或其他任何位于同一个包内的类来说,它却是可以访问的。

7.7 向上转型

      该术语的使用有其历史原因,并且是以传统的类继承图的绘制方法为基础的:将根至于页面的顶端,然后逐渐向下。

7.8 final关键字

       final用到的三种情况:数据、方法和类。

7.8.1 final数据

      若是基本数据类型,final使数值恒定不变;

      若是对象引用,final使引用恒定不变。

      一个既是static又是final的域只占据一段不能改变的存储空间。定义static,则强调只有一份;定义为final,则说明它是一个常量。

      必须在域定义处或者每个构造器中用表达式对final赋值,这正是final域在使用前总是被初始化的原因所在。

      java允许在参数列表中以声明的方式将参数指明为final。 这意味着你无法在方法中更改参数所指向的对象。

7.8.2 final方法

     使用final方法的原因有两个。第一个是把方法锁定,以防任何集成类修改它的定义。第二个原因失效率,这个早起java版本的策略,有点类似c++中的inline关键字。

     类中所有的private方法都是隐式地指定为final的。由于无法取用privagte方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但这并不能给该方法增加任何额外的意义。

7.8.3 final类

       当将某个类的整体定义为final时,这就表示你不打算继承该类,而且也不允许别人这样做。

7.9 初始化与类加载

       可以说“类的代码在初次使用时才加载”,这通常是指加载发生于创建类的第一个对象之时,但是当访问static域或static方法时,也会发生加载。

       当某个类上运行java时,所发生的第一件事情就是试图访问该类的main方法(一个static方法),于是加载器开始启动并找出该类的编译代码。在对它进行加载的过程中,编译器注意到它由一个基类(这是由关键字extends得知的),于是它继续进行加载。不管你是否打算产生一个该基类的对象,这都是要发生的。

       如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类中的static初始化即会被执行,然后是下一个导出类,以此类推。这种方法很重要,因为导出类的static初始化可能会依赖于该基类成员能否被正确初始化。

       接下来,内存被清空,所有基本类型被设置为默认值,对象引用被设置为null。然后,基类的构造器会被调用。基类的构造器和导出类的构造器一样,以相同的顺序来经历相同的过程。在基类的构造器完成以后,实例变量按其次序被初始化。最后构造器的其他部分被执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值