复用代码是Java众多引人注目的功能之一。
一般而言,实现代码重用java提供了两种方式:组合以及继承。
- 组合:新的类由现有类的对象所组成。(复用现有代码的功能,而非它的形式)
- 继承:按照现有类的类型组建新类。(在不改变现有类的基础上,复用现有类的形式并在其中添加新代码)。
组合
class Engine{ public void start(){} public void stop(){} } class Door{ public void open(){} public void close(){} } class Window{ public void rollup(){} public void rolldown(){} } public class Car { private Engine engine=new Engine(); private Door door=new Door(); private Window window=new Window(); public static void main(String[] args) { Car car=new Car(); car.engine.start(); car.door.close(); car.window.rolldown(); } }
可以看到 组合方式是显式性地在新类中放置子对象,而继承则是隐式地做。
继承
继承是所有OOP(面向对象的编程语言)语言,包括Java语言不可缺少的组成部分。
class Art{ Art(){ System.out.print("Art constructor"); } } class Drawing extends Art{ Drawing(){ System.out.print("Drawing constructor"); } } public class Cartoon extends Drawing{ public Cartoon(){ System.out.print("Cartoon constructor"); } public static void main(String[] args) { Cartoon c=new Cartoon(); } }
输出:
Art constructor
Drawing constructor
Cartoon constructor
上面的例子中,父类在子类初始化之前就完成了初始化,因为在子类初始化时默认调用了父类的无参构造器。请注意一下几点:
- 构造器不能被继承, 一个类能得到构造器,只有两个办法:编写构造器,或者使用默认无参构造器。
- 如果想要调用父类有参数的构造器,则必须在子类的构造器中显式地通过super关键字调用父类的构造器,并配以适当的参数列表(必须放在第一行)。
- 如果想要调用父类无参数的构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。
再来看个例子:
class Art{ Art(int i){ System.out.println("Art Constructor i"); } } class Drawing extends Art{ Drawing(int i){ //super(i)显式调用基类(父类)的构造器方法 super(i); System.out.println("Drawing Constructor i"); } } public class Cartoon extends Drawing { public Cartoon(int i){ //super(i)显式调用基类(父类)的构造器方法 super(i); System.out.println("Cartoon Constructor i"); } public static void main(String[] args) { Cartoon cartoon = new Cartoon(1); } }
输出:
Art constructor 1 Drawing constructor 1 Cartoon constructor 1
总结
继承和组合都能从现有类型生成新类型。组合一般是将现有类型作为新类型底层实现的一部分来加以复用,而继承复用的是接口。
在使用继承时,由于导出类具有基类接口,因此它可以向上转型至基类,这对多态来讲至关重要。
尽管面向对象编程对继承极力强调,但在开始一个设计时,一般应优先考虑使用组合(或者可能是代理),只在确实必要时才使用继承。因为组合更具灵活性。此外,通过对成员类型使用继承技术的添加技巧,可以在运行时改变那些成员对象的类型和行为。因此,可以在运行时改变组合而成的对象的行为。
在设计一个系统时,目标应该是找到或创建某些类,其中每个类都有具体的用途,而且既不会太大(包含太多的功能而难以复用),也不会太小(不添加其它功能就无法使用)。如果你的设计变得过于复杂,通过将现有类拆分为更小的部分或添加更多的对象,通常会有所帮助。
当你开始设计一个系统时,应该认识到程序开发是一种增量过程,犹如人类的学习一样,这一点很重要。程序开发依赖于实验,你可以尽己所能去分析,但当你开始执行一个项目时,你仍然无法知道所有的答案。如果将项目视作是一种有机的、进化着的生命体而去培养,而不是打算像盖摩天大楼一样快速见效,就会获得更多的成功和更迅速的回馈。继承和组合正是在面向对象程序设计中使得你可以执行这种实验的最基本的两个工具。
参考:
《Java编程思想》