复用代码是Java众多引人注目的功能之一,通过创建新类来复用代码,使用类且不破坏现有的程序代码。
组合
组合实现
将现有类型的对象置于新类型中,将该类型的作为新类型底层实现的一部分加以复用。
初始化现有类对象
在上一篇博客中提到,类中的对象引用会被初始化为null(未指定初始化值时),而如果此时为它们调用方法时,都会得到一个异常,所以要对对象进行初始化。
对对象进行初始化的位置
1)在定义对象的地方进行初始化
2)在类的构造器中进行初始化
3)在正在使用这些对象之前,这也叫作“惰性初始化”
继承
继承实现
继承复用的是接口,即导出类具有基类的接口。
初始化基类
简介
当创建一个导出类的对象时,该对象包含了一个基类的子对象,该子对象被包装在导出类对象内部。所以在导出类中正确地初始化基类子对象是十分重要的。
如何初始化
在到处类的构造器中调用基类的构造器执行初始化,并且基类构造器必须具有执行基类初始化所需要的所有知识和能力。
1. 缺省构造器(无参构造器)
如果开发者没有指定具体的基类构造器,编译器将会自动为你合成一个缺省构造器。
2. 带参数的构造器
如果类没有缺省的无参构造器,或者想调用一个带参数的基类构造器,就必须用super显示的编写调用基类构造器的语句,并配以适当的参数列表。注意:基类构造器必须是你在导出类构造器中要做的第一件事,不然编译器会提醒你的:)
class Game() {
Game(int i){
System.out.println("Game constructor");
}
}
calss BoardGame extends Game {
BoardGame(int i) {
super(i);
System.out.println("BoardGame constructor");
}
}
public class Chess extends BoardGame {
Chess(){
super(11);
System.out.println("Chess constructor");
}
public static void main(String[] args){
Chess x = Chess();
}
}
/*控制台显示结果:
Game constructor
BoardGame constructor
Chess constructor
*/
包括继承在内的初始化全程
在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的零(0和null)
对一个类进行加载的过程中,如果它有一个基类,那么解释器就会继续加载(基类)(不管你是否打算产生一个该基类的对象,这都要发生),而如果这个基类还有自身的基类,那么第二个基类就会被加载,如此类推。
接下来,根基类的static初始化被执行,然后是下一个导出类,以此类推。
至此,所有类都已被加载完毕,对象就可以被创建了。首先,创建根基类的子对象,根基类的成员进行初始化,然后调用根基类构造器(可以结合我这篇博客 初始化与清理),再接下来就是下一个导出类,以此类推。
protected关键字
基类中protected方法可以被导出类继承。最好的方式是将字段保持为private;你应当一直保留“更改底层实现”。然后通过protected方法来控制类的继承者的访问权限。
final关键字:无法修改
final数据
恒定不变的数据。对于基本类型,final使数值恒定不变;对于对象引用,final使引用恒定不变。
空白final
空白final在final的使用上提供了更大的灵活性,为此,一个类中的final字段就可以做到根据对象而有所不同。但无论何时都必须确保空白final在使用前被初始化。
final参数
在参数列表中以声明的方式将参数指明为final。这意味着你无法在方法中更改参数引用的指向。
final方法
将方法锁定,以防止继承类的修改。(在设计类时,将方法指明为final是明智的)
编译器发现final方法时根据判断直接将final方法的副本插入到当前代码段而不是调用(相当于C++中的inline)。
final类
出于某种考虑,不打算对该类的设计做任何变动,或出于安全的考虑,不希望它有自子类。
确保正确清理
如果你想要某个类清理一些东西,请显示地编写一个方法来做这件事,客户端程序员必须知晓他们必须调用这一方法且将这一清理动作置于finally子句中,以预防异常的出现。
在清理方法中还必须注意对基类清理方法和成员对象清理方法的调用顺序,以防某个子对象依赖于另一个子对象情形的放生。首先,执行类的所有特定的清理方法,其顺序同生成顺序相反(通常这要求“基类”元素仍旧存活);然后,调用基类的清理方法(通过super.function_name(参数表列))。
除了内存以外,不要依赖垃圾回收器去做任何事。如果需要进行清理最好是编写你自己的清理方法,不要依赖finalize()。
增量开发
程序开发是一种增量过程,将项目看成是一种有机的、进化的生命体去培养。你的程序不该只关心”bit”,而应该通过生成和操作各种类型的对象,用来自问题空间的术语来表现一个模型。
向上转型
程序代码可以对基类和它的导出类起作用,将导出类引用转换为基类引用的动作叫做“向上转型”。
基类名 i = 子类对象;
在向上转型的过程中,类接口中唯一可能发生的事情就是丢失方法而不是获取它们。
在组合和继承中选择
组合
通常用于想在新类中使用现有类的功能而非它的接口,即在新类中嵌入某个对象,让其实现所需的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口。为取得此效果,需在新类中嵌入一个现有类的private对象(但并非绝对,看具体开发情况)。
继承
使用某个现有类,并开发一个它的特殊版本。通常,这意味着你在使用一个通用类,并为了某种特殊需要而将其特殊化。
选择
“is-a”(是一个)的关系用继承来表达,”has-a”(有一个)的关系用组合来表达。
如果必须向上转型,则继承是必要的;否则,就该好好考虑是否需要继承。
结合使用组合和继承