1. 实现类的复用通常有两种方式
- 组合:在新的类中产生现有类的对象
- 继承:按照现有类的类型来创造新类
2. 一个特殊的方法toString()
- 所有的方法都是public的.
4. 基类初始化
5. 复用的第三种方式: 代理
- 代理: java并不直接支持他,它是继承和组合之间的一种中庸之道.
- 无论我们使用的时组合,还是继承. 都会暴露所有对象的成员方法
- 组合:在类中new一个对象, 然后该对象的所有成员方法在这个类中就都可以被使用。
- 继承:继承了父类, 那么父类的方法对这个子类来说, 更是完全可见的。
- 这两种方法都会完全暴掠被使用的那个类的成员方法, 而有的时候, 我们不希望这样, 不想父类或者被调用类的成员方法被暴露在外, 就可以采用代理的方式, 如下例:太空船需要一个控制模块
package net.mindview.reusing; public class SpaceShipControls { //velocity 速度 void up(int velocity){} void down(void left(void right(void forward(void back(void turboBoost(int velocity){} 发动 }
构造一个太空船, 可以使用组合或者继承。 我们这里使用继承的方式构造。
SpaceShip extends SpaceShipControls { private String name; public SpaceShip(String name) { this.name = name; } @Override String toString() { return name; } static void main(String[] args) { SpaceShip protector = new SpaceShip("太空号"); protector.up(2000); } }
然而, SpaceShip并非一个真正的SpaceShipControls类型。太空船是太空船, 并不是太空控制器, 只是使用了太空控制器而已。更准确的说:SpaceShip包含SpaceShipControls。而且, 一旦继承, SpaceShipControls的所有方法都在SpaceShip中暴露了。这时我们可以使用代理方式来解决这个问题:
package net.mindview.reusing; /** * 太空船代理 */ SpaceShipDelegation { String name; public SpaceShipControls controls = new SpaceShipControls(); SpaceShipDelegation(String name){ velocity){ controls.up(velocity); } velocity){ controls.down(velocity); } velocity){ controls.left(velocity); } velocity){ controls.right(velocity); } velocity){ controls.forward(velocity); } velocity){ controls.back(velocity); } int velocity){ controls.turboBoost(velocity); } main(String[] args) { SpaceShipDelegation protector = new SpaceShipDelegation(太空号210000); } }
这就是使用了代理。 主要关注上面的方法是如何传递给底层的controls对象的。 其接口与使用继承得到的接口一样了。 然而,我们使用代理时却得到了更多的控制力。
6. 如何选择组合和继承
- 组合通常是想在新类中使用现有类的功能而非他的接口。就是说,在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口。而非锁嵌入对象的接口。为取得这个效果, 应该将现有类定义为private的。看下面的这个例子:
Engine { start(){} rev(){} stop(){} } Wheel { void inflate( psi){} } Window{ rollup(){} rolldown(){} } Door{ public Window window = Window(); open(){} close(){} } Car { public Engine engine = Engine(); public Wheel[] wheels = new Wheel[4]; public Door left = new Door(),right = Door(); Car(){ for(int i=0;i<4; i++){ wheels[i] = Wheel(); } } main(String[] args) { Car car = Car(); car.left.window.rollup(); car.wheels[0].inflate(72); } }
这里面, 显示的car的组成部分, 所以使用的是组合的方式。 而如果有一个“交通工具”和car是什么关系呢? 显然他们车不是交通工具组成的。car确实一类交通工具, 所以,交通工具和car应该是继承的关系。
- 向上转型
* * 乐器 Instrument { play(){} 用乐器弹奏曲调 tune(Instrument i){ i.play(); } } 乐器的一种 Wind extends Instrument{ main(String[] args) { Wind wind = Wind(); 使用wind弹奏曲调 Instrument.tune(wind); } }
- 上面的Instrument类中,接收的Instrument的引用,在使用的时候, 我们可以将继承了Instrument的乐器传给tune方法。这称之为向上转型。
-
到底什么时候使用组合, 什么时候使用继承呢?
- 一个最清晰的判断就是, 问问自己,是否需要从新类向基类向上转型。如果需要转型, 那么就必须使用继承,否则就要好好想想了, 是否有使用集成的必要性。
7.final关键字
- final关键字的含义通常是“这是无法改变的
- 作用于成员变量, 表示该变量编译时不可变。
- 作用于成员变量, 和public static一起使用, 表示全局常量
- 成员变量是对象类型时使用final表示, 对象的引用不可改变。
- 空白final
- 所谓“空白final“指的是, 在定义final变量的时候不给他赋初始值, 而是在构造函数中为其赋值。这样就提供了更大的灵活性。使用方法如下所示:
Poppet { private i; Poppet( i){ this.i = i; } } BlankFinal { 定义的时候赋初始值 private final int i = 0; 空白final j; 空白final引用 final Poppet p; BlankFinal(){ j = 1; p = new Poppet(); } public BlankFinal( x){ j = x; p = Poppet(x); } main(String[] args) { BlankFinal(); new BlankFinal(); } }
- 所谓“空白final“指的是, 在定义final变量的时候不给他赋初始值, 而是在构造函数中为其赋值。这样就提供了更大的灵活性。使用方法如下所示:
- 在参数列表中以声明的方式将参数指明为final
- 如果final作用的参数是对象,则表明在方法中无法更改参数引用所指向的对象。
- 如果final作用的是基本类型的参数, 则表示不可以改变传递过来的参数值。
Gizmo{ spin() {} } final作用于方法参数 FinalArguments { final作用于对象类型的方法参数 with(final Gizmo g) { 在这里不可以对传递过来个g重新指向新的引用 g = new Gizmo(); } without(Gizmo g){ g = Gizmo(); g.spin(); } final作用于基本类型的方法参数 void f(final i) { 在这里不可以对i重新赋值 i++; int g(final return i+; } main(String[] args) { FinalArguments bf = FinalArguments(); bf.with(null); bf.without(); } }
- final方法
- final 类
- 当某各类被定义为final时,不可被继承
忠告:再设计类的时候, 将方法定义为final的,应该说是明智的。你可能会觉得,没人想要覆盖你的方法,但预见类是如何被复用是很困难的, 特别是对于一个通用类而言,更是如此。如果你将方法定义为final的, 可以防止其他程序员在项目中通过继承来复用你的类。