第7章 复用

对于像 C 语言等面向过程语言来说,“复用”通常指的就是“复制代码”。任何语言都可通过简单复制来达到代码复用的目的,但是这样做的效果并不好。Java 围绕“类”(Class)来解决问题。我们可以直接使用别人构建或调试过的代码,而非创建新类、重新开始。

1. 组合

仅需要把对象的引用(object references)放置在一个新的类里,这就使用了组合。

(1)当对象被定义时。这意味着它们总是在调用构造函数之前初始化。
(2)在该类的构造函数中。
(3)在实际使用对象之前。这通常称为延迟初始化。在对象创建开销大且不需要每次都创建对象的情况下,它可以减少开销。
(4)使用实例初始化。

2. 继承

在创建类时总是要继承,因为除非显式地继承其他类,否则就隐式地继承 Java 的标准根类对象(Object)。
使用关键字 extends 后跟基类的名称。当你这样做时,你将自动获得基类中的所有字段和方法。

(1)初始化基类

   从外部看,新类与基类具有相同的接口,可能还有一些额外的方法和字段。但是继承并不只是复制基类的接口。当你创建派生类的对象时,它包含基类的子对象。这个子对象与你自己创建基类的对象是一样的。只是从外部看,基类的子对象被包装在派生类的对象中。
   必须正确初始化基类子对象,而且只有一种方法可以保证这一点 : 通过调用基类构造函数在构造函数中执行初始化,该构造函数具有执行基类初始化所需的所有适当信息和特权。Java 自动在派生类构造函数中插入对基类构造函数的调用。(自动调用基类的的构造函数)

(2)带参数的构造函数

如果没有无参数的基类构造函数,或者必须调用具有参数的基类构造函数,则必须使用 super 关键字和适当的参数列表显式地编写对基类构造函数的调用:

委托

Java不直接支持的第三种重用关系称为委托,但是但是开发工具常常支持。委托介于继承和组合之间,因为你将一个成员对象放在正在构建的类中(比如组合),但同时又在新类中公开来自成员对象的所有方法(比如继承)。

保持适当的清理

在Java中,你无法知道垃圾收集器何时会被调用,甚至它是否会被调用。因此,如果你想为类清理一些东西,必须显式地在 *finally *子句中放置此类清理来防止异常。
除了内存回收外,你不能依赖垃圾收集来做任何事情。如果希望进行清理,可以使用自己的清理方法,不要使用 finalize()。

名称隐藏

如果 Java 基类的方法名多次重载,则在派生类中重新定义该方法名不会隐藏任何基类版本。不管方法是在这个级别定义的,还是在基类中定义的,重载都会起作用:
当你打算重写一个方法时,你可以选择添加这个注释,如果你不小心用了重载而不是重写,编译器会产生一个错误消息。

组合和继承的选择

这种“是一个”的关系是用继承来表达的,而“有一个“的关系则用组合来表达

protected

在实际项目中,却经常想把一个事物尽量对外界隐藏,而允许派生类的成员访问。
关键字 protected 就起这个作用。它表示“就类的用户而言,这是 private 的。但对于任何继承它的子类或在同一包中的类,它是可访问的。”(protected 也提供了包访问权限)

尽管可以创建 protected 属性,但是最好的方式是将属性声明为 private 以一直保留更改底层实现的权利(仅有基类可以直接修改)。然后通过 protected 控制类的继承者的访问权限。

向上转型

继承最重要的方面不是为新类提供方法。它是新类与基类的一种关系。简而言之,这种关系可以表述为“新类是已有类的一种类型”

继承图中派生类转型为基类是向上的,所以通常称作向上转型。因为是从一个更具体的类转化为一个更一般的类,所以向上转型永远是安全的。也就是说,派生类是基类的一个超集。它可能比基类包含更多的方法,但它必须至少具有与基类一样的方法。在向上转型期间,类接口只可能失去方法,不会增加方法。这就是为什么编译器在没有任何明确转型或其他特殊标记的情况下,仍然允许向上转型的原因。

再论组合和继承

一种判断使用组合还是继承的最清晰的方法是问一问自己是否需要把新类向上转型为基类。如果必须向上转型,那么继承就是必要的

final关键字

(1)final数据

final修饰基本类型,则值不可修改
final修饰对象,则引用不可修改,但是引用的对象内部值可以修改
按照惯例,带有恒定初始值的 final static 基本变量(即编译时常量)命名全部使用大写,单词之间用下划线分隔。

(2)final参数

在参数列表中,将参数声明为 final 意味着在方法中不能改变参数指向的对象或基本变量。

(3) final方法

使用 final 方法的原因有两个。第一个原因是给方法上锁,防止子类通过覆写改变方法的行为。这是出于继承的考虑,确保方法的行为不会因继承而改变。
过去建议使用 final 方法的第二个原因是效率。在早期的 Java 实现中,如果将一个方法指明为 final,就是同意编译器把对该方法的调用转化为内嵌调用。

(4)final类

当说一个类是 final (final 关键字在类定义之前),就意味着它不能被继承。