Thinking in Java 第七章------复用类(2)
来源:互联网 发布:海马模拟器for mac 编辑:程序博客网 时间:2024/05/18 06:21
Thinking in Java 第七章——复用类(1)
Thinking in Java 第七章——复用类(2)
Thinking in Java 第七章——复用类(3)
三、代理
Java没有提供对代理的直接支持.这是继承与组合的中庸之道。因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该对象的所有方法(就像继承)。
例如,太空船需要一个控制模块:
public 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 SpaceShip extends SpaceShipControls { private String name; public SpaceShip(String name) { this.name = name; } public String toString() { return name; } public static void main(String[] args) { SpaceShip protector = new SpaceShip("NSEA Protector"); protector.forward(100); }} ///:~
然而SpaceShip
并非真正的SpaceShipControls
类型,即便你可以告诉SpaceShip
向前运动forward()
。更准确的讲,SpaceShip
包含SpaceShipControls
,与此同时,SpaceShipControls
中的所有方法在SpaceShip
中都暴露了出来。代理解决了这个难题:
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
,而其接口由此也就与使用继承得到的接口相同了。但是我们使用代理时,可以拥有更多的控制力,因为我们可以选择只提供在成员对象中方法的子集。
四、结合使用组合和继承
给出一个例子:
import static net.mindview.util.Print.*;class Plate { Plate(int i) { print("Plate constructor"); }}class DinnerPlate extends Plate { DinnerPlate(int i) { super(i); print("DinnerPlate constructor"); }} class Utensil { Utensil(int i) { print("Utensil constructor"); }}class Spoon extends Utensil { Spoon(int i) { super(i); print("Spoon constructor"); }}class Fork extends Utensil { Fork(int i) { super(i); print("Fork constructor"); }} class Knife extends Utensil { Knife(int i) { super(i); print("Knife constructor"); }}// A cultural way of doing something:class Custom { Custom(int i) { print("Custom constructor"); }} public class PlaceSetting extends Custom { private Spoon sp; private Fork frk; private Knife kn; private DinnerPlate pl; public PlaceSetting(int i) { super(i + 1); sp = new Spoon(i + 2); frk = new Fork(i + 3); kn = new Knife(i + 4); pl = new DinnerPlate(i + 5); print("PlaceSetting constructor"); } public static void main(String[] args) { PlaceSetting x = new PlaceSetting(9); }} /* Output:Custom constructorUtensil constructorSpoon constructorUtensil constructorFork constructorUtensil constructorKnife constructorPlate constructorDinnerPlate constructorPlaceSetting constructor*///:~
虽然编译器强制你去初始化基类,并且要求你要在构造器起始处就这么做,但是它并不监督你必须将成员对象也初始化。
4.1确保正确的清理
Java中没C++中析构函数的概念。析构函数是一种在对象被销毁时可以被自动调用的函数。其原因可能是因为在Java中,我们的习惯只是忘掉而不是销毁对象,并且让垃圾回收其在必要时释放其内存。
通常这样做事好事,但是有时候,类可能需要在其生命周期内执行一些必须的清理活动。因为垃圾回收器不知道何时会被调用,或者它是够将被调用,因此如果你想要某个类清理一些东西,就必须显示的编写一个特殊方法来做这件事,并要确保客户端程序猿知道必须调用这一方法。
例如:
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 constructorShape constructorDrawing Line: 0, 0Shape constructorDrawing Line: 1, 1Shape constructorDrawing Line: 2, 4Shape constructorDrawing CircleShape constructorDrawing TriangleCombined constructorCADSystem.dispose()Erasing TriangleShape disposeErasing CircleShape disposeErasing Line: 2, 4Shape disposeErasing Line: 1, 1Shape disposeErasing Line: 0, 0Shape disposeShape dispose*///:~
此系统中的一切都是某种
Shape
,每个类都覆写Shape
的dispose()
方法,并运用super来调用该方法的基类版本。无论
try
块是怎么样退出的,finally
子句中的代码总是要被执行的。这里的finally
代表的是“无论发生什么事情,一定要为x调用dispose()
方法”。在清理方法中还需要注意对基类清理方法和成员对象清理方法的调用顺序:首先,执行类的所有特定的清理动作,其顺序同生成书序相反。然后,就如示范那样,调用基类的清理方法。
4.2名称屏蔽
如果Java的基类拥有某个已经被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽其在基类中的任何版本。因此无论是在该层基类或者它的基类中对方法进行定义,重载机制都可以正常的工作。
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)*///:~
可以看到,虽然在Bar引入了一个新的重载方法,但是在Bar
中Homer
的所有重载方法欧式可以使用的。
Java SE5中新增加了@override
注解,它并不是关键字,但是可以把它当做关键字来使用。当腰覆写某个方法时候,就可以选择添加这个注解,在你不留心重载而非覆写该方法时,编译器就会生成错误信息,提示你。
五、在组合与继承之间选择
组合和继承都允许在新的类中放置子对象,组合是显式这么做的,而继承是隐式这么做的
组合技术通常用于想在新类中使用现有类的功能,而非它的接口这种情形。即,在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是新类定义的接口,而非所嵌入对象的接口。
在继承的时候,使用某个现有类,并开发一个它的特殊版本。通常这意味着你再使用一个通用类,并为了某种特殊化需要而将其特殊化。
“is-a”
(是一个)关系用继承来表达;“has-a”
(有一个)关系用组合来表达。到底是该使用组合还是继承,一个最清晰的判断方法就是问一问自己,是够需要从新类向基类进行向上转型。如果必须是,则继承是必要的。但如果不需要,则应当好好考虑自己是够需要继承。
在理想世界中,仅靠关键字
private
就已经足够了,但是在实际项目中,经常会想要将某些事物尽可能对这个世界隐藏起来,但是任然允许导出类的成员访问他们。关键字protected
就是其这个作用。它指明,就类用户而言,这是private
的,但对于任何继承于此类的导出类或者他任何位于同一个包中类来说,他确实可以访问的。(protected
提供了包访问全权限。)
- Thinking in Java 第七章------复用类(2)
- Thinking in Java:第七章-复用类
- Thinking In Java笔记(第七章 复用类)
- Thinking in Java 第七章------复用类(1)
- Thinking in Java 第七章------复用类(3)
- thinking in java 笔记 思维导图 第七章 复用类
- Thinking in Java——第七章-复用类
- day6:《Thinking in Java》笔记第七章---复用类
- Thinking in Java学习笔记 第七章:复用类
- Thinking in Java 读书笔记 第七章 复用类(继承、组合)
- #Thinking in Java阅读笔记# 第七章 复用类
- Thinking in java (第三版)第七章 多态性(Polymorphism)
- Thinking in Java(第四版)习题--第七章
- Thinking in Java第七章阅读小结
- Thinking in Java 笔记(第七章 多态)
- Thinking in Java第三版读书笔记-第七章:多态性
- 《Thinking in Java》学习笔记——第七章:多形性
- thinking in java 阅读笔记 第七章 多形性
- 使用Rational Rose进行用例图和活动图
- 第九周项目4-广义表算法库及应用
- 一些常用的工具方法
- PHP 获取网页内容的三种方法
- 圣杯布局
- Thinking in Java 第七章------复用类(2)
- 增强现实(AR)+Unity 虚拟按键官方底层图片研究
- Web前端应该从哪些方面来优化网站
- RCNN学习笔记(1):Rich feature hierarchies for accurate object detection and semantic segmentation
- C#自定义控件之-自定义MessageBox
- java.lang.NoClassDefFoundError
- qsort函数的用法
- JAVA AIO 服务器与客户端实现示例
- MSVOD笔记