您的位置:首页 > 编程语言 > Java开发

JAVA编程思想(3) - 复用类(一)

2014-08-23 21:27 295 查看


复用类


组合语法

组合只需将对象引用放置于新类中即可。例如:假设你需要某个对象,它要具有多个String对象,几个基本类型数据,以及另一个类的对象。对于非基本类型的对象,必须将其引用置于新的类中,但可以直接定义基本类型数据。


继承语法

继承是所有OOP语言和Java语言不可缺少的组成部分。当创建一个类时,总是在继承,因此,除非已明确指出要从其他类中继承,否则就是隐式的从Java的标准根类Obiect进行继承。


初始化基类

由于现在涉及基类和导出类这两个类,而不是一个类,所以要试着想象导出类产生的结果对象,会有些困惑。
从外部看,它就像一个与基类具有相同接口的新类,或许还会有一些额外的方法或域,但继承并不只是复制基类的接口。
当创建一个导出类的对象时,该对象包含一个基类的子对象,这个子对象与你用基类直接创建的对象是一样的。二者区别在于,后者来自于外部,而基类的子对象被包装在导出类对象内部。
对基类初始化的唯一方法是:在构造器中调用基类构造器来执行初始化,而基类构造器具有执行基类初始化所需要的所有知识和能力。
构造过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问它之前,就已经完成初始化。即使没有显示的构造器,编译器也会为你合成一个默认的构造器,该构造器将调用基类的构造器。


代理

除了继承和组合的第三种类之间的关系是:代理。
Java并没有提供对它的直接支持。这是继承与组合之间的中庸之道。
因为我们将一个对象置于所要构造的类中(就像组合),但与此同时我们也在新类中暴露了该成员对象的所有方法(就像继承)。
代理解决对象成员方法暴露的问题。
//: reusing/SpaceShipDelegation.java

public class SpaceShipDelegation {
private String name;
private SpaceShipControls controls =
new SpaceShipControls();
public SpaceShipDelegation(String name) {
this.name = name;
}
// Delegated methods:
public void back(int velocity) {
controls.back(velocity);
}
public void down(int velocity) {
controls.down(velocity);
}
public void forward(int velocity) {
controls.forward(velocity);
}
public void left(int velocity) {
controls.left(velocity);
}
public void right(int velocity) {
controls.right(velocity);
}
public void turboBoost() {
controls.turboBoost();
}
public void up(int velocity) {
controls.up(velocity);
}
public static void main(String[] args) {
SpaceShipDelegation protector =
new SpaceShipDelegation("NSEA Protector");
protector.forward(100);
}
} ///:~

可以看到,上面的方法是如何传递给了底层controls对象,而其接口由此也就与使用继承的接口相同了,但是代理可以拥有更多的控制力,因为我们可以选择我们想要暴露的方法。


结合使用组合和继承

虽然编译器强制你初始话基类,并且要求你要在构造器起始处就要这么做,但是它并不监督你必须将成员对象也初始化,因此在这点你自己必须注意


确保正确清理

Java中没有C++中的析构函数,析构函数是一种在对象被销毁时可以被自动调用的函数,其原因是因为在Java中,我们的习惯只是忘记而不摧毁对象,并且让垃圾回收器在必要的时候释放其内存
但有时类可能在其生命周期内执行一些必需的清理活动。而你并不知道垃圾回收器何时会被调用,或者它是否被调用。因此当你想要清理一些东西的时候,就必须显示地编写一个特殊方法来做这件事,并确保客户端程序员知晓他们必须要调用这一方法。
//: reusing/CADSystem.java
// Ensuring proper cleanup.
package reusing;
import static net.mindview.util.Print.*;

class Shape {
Shape(int i) { print("Shape constructor"); }
void dispose() { print("Shape dispose"); }
}

class Circle extends Shape {
Circle(int i) {
super(i);
print("Drawing Circle");
}
void dispose() {
print("Erasing Circle");
super.dispose();
}
}

class Triangle extends Shape {
Triangle(int i) {
super(i);
print("Drawing Triangle");
}
void dispose() {
print("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;
print("Drawing Line: " + start + ", " + end);
}
void dispose() {
print("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(1);
t = new Triangle(1);
print("Combined constructor");
}
public void dispose() {
print("CADSystem.dispose()");
// The order of cleanup is the reverse
// of the order of initialization:
t.dispose();
c.dispose();
for(int i = lines.length - 1; 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();
}
}
} /* Output:
Shape constructor
Shape constructor
Drawing Line: 0, 0
Shape constructor
Drawing Line: 1, 1
Shape constructor
Drawing Line: 2, 4
Shape constructor
Drawing Circle
Shape constructor
Drawing Triangle
Combined constructor
CADSystem.dispose()
Erasing Triangle
Shape dispose
Erasing Circle
Shape dispose
Erasing Line: 2, 4
Shape dispose
Erasing Line: 1, 1
Shape dispose
Erasing Line: 0, 0
Shape dispose
Shape dispose
*///:~

每个类都覆写Shape的dispose()方法,并运用super来调用该方法的基类版本。每个类都有自己的dispose()方法来将未存于内存之中的东西恢复到对象存在之前。
关键字try表示, 下面的块(用一组大括号括起来的范围)是所谓的保护区,这就意味着它需要被特殊处理。其中一项特殊处理是无论try块是怎样退出的,保护区后的finally子句中的代码总要被执行。这里的finally子句表示的是“无论发生什么事,一定要为x调用dispose()”。
许多情况下,清理并不是问题,仅需让垃圾回收器完成该动作就行了,但当必须亲自处理的时候,就需要我们多做努力并多加小心,而且垃圾回收器可能无法按照我们想要的顺序取回收对象,它可能以任何它想要的顺序。所以,最好的办法是除了内存以外,不能依赖垃圾回收器去做任何事情。如果需要清理的话,最好是编写你自己的方法。


名称屏蔽

如果Java中的基类拥有某个已被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本(这点与C++不同)。因此,无论是在该层或者它的基类中对方法进行定义,重载机制都可以正常工作。
//: reusing/Hide.java
// Overloading a base-class method name in a derived
// class does not hide the base-class versions.
import static net.mindview.util.Print.*;

class Homer {
char doh(char c) {
print("doh(char)");
return 'd';
}
float doh(float f) {
print("doh(float)");
return 1.0f;
}
}

class Milhouse {}

class Bart extends Homer {
void doh(Milhouse m) {
print("doh(Milhouse)");
}
}

public class Hide {
public static void main(String[] args) {
Bart b = new Bart();
b.doh(1);
b.doh('x');
b.doh(1.0f);
b.doh(new Milhouse());
}
} /* Output:
doh(float)
doh(char)
doh(float)
doh(Milhouse)
*///:~

虽然Bart引入一个新的重载方法(在C++中若要完成这项工作则需要屏蔽基类方法),但是在Bart中Homer的所有的重载方法都是可以使用的。使用与基类完全相同的特征签名及返回类型来覆盖具有相同的名称方法,是一件极其平常的事情,但它也是令人迷惑不解(C++不允许这么做)
@Override 注解,不是关键字,但是可以理解成关键字,当你想要覆写某个方法的时候,可以选择这个注解,在你不留心重载而非覆写这个方法的时候,编译器会报错。这样@Override注解可以防止你不想重载时而意外的进行了重载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  学习