wordpress在本地建站,网站已付款方式,上海seo推广服务,牡丹区建设局网站1 代理 第三种关系称为代理#xff0c;这是继承与组合之间的中庸之道#xff0c;因为我们将一个成员对象置于所要构造的类中#xff08;就像组合#xff09;#xff0c;但与此同时我们在新类中暴露了该成员对象的所有方法#xff08;就像继承#xff09;。例如#xff…1 代理 第三种关系称为代理这是继承与组合之间的中庸之道因为我们将一个成员对象置于所要构造的类中就像组合但与此同时我们在新类中暴露了该成员对象的所有方法就像继承。例如太空船需要一个控制模版
/*** 宇宙飞船控制模版*/
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对象而其接口由此也就与使用继承得到的接口相同了。但是我们使用代理时可以拥有更多的控制力因为我们可以选择只提供在成员对象中的方法的某个子集。
2 结合使用组合和继承 同时使用组合和继承是很常见的事。下例就展示了同时使用这两种技术并配以必要的构造器初始化来创建更加复杂的类
/*** 盘*/
class Plate {Plate(int i) {System.out.println(Plate constructor);}
}/*** 餐盘*/
class DinnerPlate extends Plate {DinnerPlate(int i) {super(i);System.out.println(DinnerPlate constructor);}
}/*** 餐具*/
class Utensil {Utensil(int i) {System.out.println(Utensil constructor);}
}/*** 勺子*/
class Spoon extends Utensil {Spoon(int i) {super(i);System.out.println(Spoon constructor);}
}/*** 叉子*/
class Fork extends Utensil {Fork(int i) {super(i);System.out.println(Fork constructor);}
}/*** 刀具*/
class Knife extends Utensil {Knife(int i) {super(i);System.out.println(Knife constructor);}
}/*** 自定义*/
class Custom {Custom(int i) {System.out.println(Custom constructor);}
}/*** 餐位*/
public class PlaceSetting extends Custom {private Spoon sp;private Fork frk;private Knife kn;private DinnerPlate pl;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);System.out.println(PlaceSetting constructor);}public static void main(String[] args) {PlaceSetting x new PlaceSetting(9);}
} 虽然编译器强制你去初始化基类并且要求你要在构造器起始处就要这么做但是它并不监督你必须将成员对象也初始化因此在这一点上你自己必须时刻注意。 这些类如此清晰地分离着实使人惊讶。甚至不需要这些方法的源代码就可以复用这些代码我们至多只需要导入一个包。对于继承与组合来说都是如此。
3 确保正确清理 java中没有C中析构函数的概念。析构函数是一种在对象被销毁时可以被自动调用的函数。其原因可能是因为在java中我们的习惯只是忘掉而不是销毁对象并且让垃圾回收器在必要时释放其内存。 通常这样做是好事但有时类可能要在其声明周期内执行一些必需的清理活动。你并不知道垃圾回收器何时将会被调用或者它是否将被调用。因此如果你想要某个类清理一些东西就必须显示地编写一个特殊方法来做这件事并要确保客户端程序员知晓他们必须要调用这一方法。必须将这一清理工作置于finally子句之中以预防异常的出现。 请思考一下下面这个能在屏幕上绘制图案的计算机辅助设计系统示例
/*** 形状*/
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);}void dispose() {System.out.println(Erasing Circle);super.dispose();}
}/*** 三角形*/
class Triangle extends Shape {Triangle(int i) {super(i);System.out.println(Drawing Triangle);}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);}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];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);System.out.println(Combined constructor);}public void dispose() {System.out.println(CADSystem的dispose());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();}}} 此系统中的一切都是某种ShapeShape自身就是一种Object因为Shape继承自根类Object。每个类都覆写Shape的dispose()方法并运用super来调用该方法的基类版本。尽管对象生命期中任何被调用的方法都可以做一些必需的清理工作但是Circle、Triangle和Line这些特定的Shape类仍然都带有可以进行“绘制”的构造器。每个类都有自己的dispose()方法将存于内存之中的东西恢复到对象存在之前的状态。 在main()中可以看到try和finally这两个关键字。关键字try表示下面的块用一组大括号括起来的范围是所谓的保护区guarded region这意味着他需要被特殊处理。其中一项特殊处理就是无论try块是怎么退出的保护区后的finally子句中的代码总是要被执行的。这里finally子句表示的是“无论发生什么事一定要为x调用dispose()”。 在清理方法dispose()中还必须注意对基类清理方法和成员对象清理方法的调用顺序以防某个子对象依赖于另一个子对象情形的发生。一般而言所采用的形式应该与C编译器在其析构函数上所施加的形式相同首先执行类的所有特定的清理动作其顺序同生成顺序相反通常这就要求基类元素仍旧存活然后就如我们所示范的那样调用基类的清理方法。 许多情况下清理并不是问题仅需让垃圾回收器完成该动作就行。但当必须亲自处理清理时就得多做努力并多加小心。因为一旦涉及垃圾回收能够信赖的事就不会很多了。垃圾回收器可能永远也无法被调用即使被调用它也可能以任何它想要的顺序来回收对象。最好的办法是除了内存以外不能依赖垃圾回收器去做任何事。如果需要进行清理最好是编写自己的清理方法但不要使用finalize()。
4 名称屏蔽 如果java的基类拥有某个已被多次重载的方法名称那么在导出类中重新定义该方法名并不会屏蔽其在基类中的任何版本这一点与C不同。因此无论是在该层或者它的基类中对方法进行定义重载机制都可以正常工作
class Homer {char doh(char c) {System.out.println(doh(char));return d;}float doh(float f) {System.out.println(doh(float));return 1.0f;}
}class Milhouse {
}class Bart extends Homer {void doh(Milhouse m) {System.out.println(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());}
} 可以看到虽然Bart引入了一个新的重载方法C中若要完成这项工作则需要屏蔽基类方法但是在Bart中Homer的所有重载方法都是可用的。使用与基类完全相同的特征签名及返回类型来覆盖具有相同名称的方法是一件极其平常的事。但它也令人迷惑不解。 java SE5新增了Override注解它并不是关键字但是可以把它当做关键字使用。当你想要覆写某个方法时可以选择添加这个注解在你不留心重载而并非覆写了该方法时编译器就会生成一条错误消息
class Lisa extends Homer {Overridevoid doh(Milhouse m) {System.out.println(doh(Milhouse));}
} 这样Override注解可以防止你在不想重载时而意外地进行了重载。