文章目录
复用
8.3委托
Java不直接支持的第三种重用关系称为委托。这介于继承和组合之间,因为将一个成员对象放在正在构建的类中(比如组合),但同时又在新类中公开来自成员对象的所有方法(比如继承)。例如,宇宙飞船需要一个控制模块:
class SpaceShipControls {
void up(int velocity) {}
void down(int velocity) {}
void left(int velocity) {}
void right(int velocity) {}
void forward(int velocity) {}
void back(int velocity) {}
void turboBoost() {}
}
public class SpaceShipDelegation {
private String name;
private SpaceShipControls controls = new SpaceShipControls();
public SpaceShipDelegation(String name) {
this.name = name;
}
// Delegated methods:
public void up(int velocity) {
conrols.up(velocity);
}
public void down(int velocity) {
conrols.down(velocity);
}
public void left(int velocity) {
conrols.left(velocity);
}
public void right(int velocity) {
conrols.right(velocity);
}
public void forward(int velocity) {
conrols.forward(velocity);
}
public void back(int velocity) {
conrols.back(velocity);
}
public void turboBoost() {
conrols.turboBoost();
}
public static void main(String[] args) {
SpaceShipDelegation protector = new SpaceShipDelegation("NSEA Protector");
protector.forward(100);
}
}
8.4结合组合与继承
8.4.1保证适当的清理
有时,类可能在其生命周期中执行一些需要清理的活动。由于无法知道垃圾收集器何时会被调用,甚至它是否会被调用。因此,如果想为类清理一些东西,必须显式地编写一个特殊的方法来完成它,并确保客户端程序员知道他们必须调用这个方法。例如,在屏幕上绘制图片的计算机辅助设计系统:
// Ensuring proper cleanup
class shape {
Shape(int i) {
System.out.println("Shape constructor");
}
void dispose() {
System.out.println("Shape dispose");
}
}
class Circle extends Shape {
Circle(int i) {
super(i);
System.out.println("Drawing Circle");
}
@Override
void dispose() {
System.out.println("Erasing Circle");
super.dispose();
}
}
class Triangle extends Shape {
Triangle(int i) {
super(i);
System.out.println("Drawing Triangle");
}
@Override
void dispose() {
System.out.println("Erasing Triangle");
super.dispose();
}
}
class Line extends Shape {
private int start, end;
Line(int start, int end) {
super(start);
this.start = start;
this.end = end;
System.out.println("Drawing Line: " + start + ", " + end);
}
@Override
void dispose() {
System.out.println("Erasing Line: " + start + ", " + end);
super.dispose();
}
}
public class CADSystem extends Shape {
private Circle c;
private Triangle t;
private Line[] lines = new Line[3];
public CADSystem(int i) {
super(i + 1);
for (int j = 0; j < lines.length; j++) {
lines[j] = new Line(j, j * j);
}
c = new Circle(i);
t = new Triangle(i);
System.out.println("Combined constructor");
}
@Override
public void dispose() {
System.out.println("CADSystem.dispose()");
// The order of cleanup is the reverse of the order of initialization
t.dispose();
c.dispose();
for (int i = lines.length; i >= 0; i--) {
lines[i].dispose();
}
super.dispose();
}
public static void main(String[] args) {
CADSystem x = new CADSystem(47);
try {
// Code and exception handling...
} finally {
x.dispose();
}
}
}
8.5组合与继承的选择
当想在新类中包含一个已有类的功能时,使用组合,而非继承。也就是说,在新类中嵌入一个对象(通常是私有的),以实现其功能。新类的使用者看到的是所定义的新类的接口,而非嵌入对象的接口。
有时,让类的用户直接访问到新类中的组合成分是有意义的。只需将成员对象声明为public
即可(可以把这当做半委托的一种)。成员对象隐藏了具体实现,所以这是安全的。当用户知道正在组装一组部件时,会使得接口更加容易理解。
是一个的关系是用继承来表达的,而有一个的关系则用组合来表达。
8.7向上转型
因为是从一个更具体的类转化为一个更一般的类,所以向上转型永远是安全的。也就是说,派生类是基类的一个超集。它可能比基类包含更多的方法,但它必须至少具有与基类一样的方法。在向上转型期间,类接口只可能失去方法,不会增加方法。这就是为什么编译器在没有任何明确转型或其他特殊标记的情况下,仍然允许向上转型的原因。
8.7.1再论组合和继承
继承其实不太常用,除非确实使用继承是有帮助的。一种判断使用组合还是继承的最清晰的方法是问一问自己是否需要把新类向上转型为基类。
8.8final
关键字
8.8.1final
数据
一个被
static
和final
同时修饰的属性只会占用一段不能改变的存储空间。
当用final
修饰对象引用而非基本类型时,其含义会有一点令人困惑。对于基本类型,final
使数值恒定不变,而对于对象引用,final
使引用恒定不变。一旦引用被初始化指向了某个对象,它就不能改为指向其他对象。但是,对象本身是可以修改的,java没有提供将任意对象设为常量的方法。这一限制同样适用于数组,数组也是对象。
8.9类初始化和加载
每个类的编译代码都存在于它自己独立的文件中。该文件只有在使用程序代码时才会被加载。一般可以说类的代码在首次使用时加载。这通常是指创建类的第一个对象,或是访问了类的
static
属性或方法。构造器也是一个static
方法尽管它的static
关键字是隐式的。因此,准确地说,一个类当它任意一个static
成员被访问时,就会被加载。