当前位置: 首页 > news >正文

复用类(2):代理、结合使用组合和继承

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();}}}

        此系统中的一切都是某种Shape(Shape自身就是一种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注解可以防止你在不想重载时而意外地进行了重载。  

相关文章:

复用类(2):代理、结合使用组合和继承

1 代理 第三种关系称为代理&#xff0c;这是继承与组合之间的中庸之道&#xff0c;因为我们将一个成员对象置于所要构造的类中&#xff08;就像组合&#xff09;&#xff0c;但与此同时我们在新类中暴露了该成员对象的所有方法&#xff08;就像继承&#xff09;。例如&#xff…...

浅谈云计算07 | 云安全机制

云计算安全机制 一、引言二、加密技术&#xff1a;数据的隐形护盾三、散列机制&#xff1a;数据完整性的忠诚卫士四、数字签名&#xff1a;数据来源与真伪的鉴定专家五、公钥基础设施&#xff08;PKI&#xff09;&#xff1a;信任的基石六、身份与访问管理&#xff08;IAM&…...

【机器学习】零售行业的智慧升级:机器学习驱动的精准营销与库存管理

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 在当今数字化浪潮汹涌澎湃的时代&#xff0c;零售行业正站在转型升级的十字路口。市场竞争的白热化使得企业必须另辟蹊径&#xff0…...

深入理解 Entity、VO、QO、DTO 的区别及其在 MVC 架构中的应用

文章背景 在现代软件开发中&#xff0c;我们经常会接触到各种数据结构的概念&#xff0c;比如 Entity、VO&#xff08;Value Object&#xff09;、QO&#xff08;Query Object&#xff09;、DTO&#xff08;Data Transfer Object&#xff09;等。这些概念尽管看似相似&#xff…...

vue集成高德地图API实现坐标拾取功能

安装与配置&#xff1a; 组件 | vue-amapDescriptionhttps://elemefe.github.io/vue-amap/#/zh-cn/introduction/install简介 | vuemap/vue-amap简介https://vue-amap.guyixi.cn/zh-cn/introduction/introduction.html ​​​​我的应用 | 高德控制台高德开放平台官网控…...

Spring Boot Actuator 详细介绍

Spring Boot Actuator 详细介绍 1. 简介 Spring Boot Actuator 是 Spring Boot 提供的一个用于监控和管理应用程序的强大功能模块。它可以帮助我们了解应用程序的运行状况、指标收集、环境信息、日志级别管理等。 2. 添加依赖 2.1 在 pom.xml 中添加以下依赖&#xff1a; …...

联通用户管理系统(一)

#联通用户管理系统&#xff08;一&#xff09; 1.新建项目 如果你是windows的话&#xff0c;界面应该是如下的&#xff1a; 2.创建app python manage.py startapp app01一般情况下&#xff1a;我们是在pycharm的终端中运行上述指令&#xff0c;但是pychrm中为我们提供了工具…...

go chan底层分析

go chan底层分析 底层源码hchanmakechan 方法 环形队列阻塞机制向管道写数据流程图源码 从管道读数据流程图源码 关闭通道 底层源码 hchan type hchan struct {qcount uint // 当前队列中剩余元素个数dataqsiz uint // 环形队列长度&#xff0c;即可以…...

idea上git log面板的使用

文章目录 各种颜色含义具体的文件的颜色标签颜色&#x1f3f7;️ 节点和路线 各种颜色含义 具体的文件的颜色 红色&#xff1a;表示还没有 git add 提交到暂存区绿色&#xff1a;表示已经 git add 过&#xff0c;但是从来没有 commit 过蓝色&#xff1a;表示文件有过改动 标…...

WOA-Transformer鲸鱼算法优化编码器时间序列预测(Matlab实现)

WOA-Transformer鲸鱼算法优化编码器时间序列预测&#xff08;Matlab实现&#xff09; 目录 WOA-Transformer鲸鱼算法优化编码器时间序列预测&#xff08;Matlab实现&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现WOA-Transformer鲸鱼算法优化编…...

dock 制作 python环境

报错 :Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 解决方法 配置加速地址 vim /etc/docker/daemon.json 添加以下内容 { "registry-mirror…...

2025第3周 | json-server的基本使用

目录 1. json-server是什么&#xff1f;2. json-server怎么用&#xff1f;2.1 安装2.2 创建db.json2.3 启动服务2.4 查看效果 3. 前端进行模拟交互3.1 创建demo.html3.2 创建demo.js 2025&#xff0c;做想做的事&#xff0c;读想读的书&#xff0c;持续学习&#xff0c;自律生活…...

Autodl转发端口,在本地机器上运行Autodl服务器中的ipynb文件

通过 SSH 隧道将远程端口转发到本地机器 输入服务器示例的SSH指令和密码&#xff0c;将远程的6006端口代理到本地 在服务器终端&#xff0c;激活conda虚拟环境 conda activate posecnnexport PYOPENGL_PLATFORMegljupyter notebook --no-browser --port6006 --allow-root从…...

flutter Get GetMiddleware 中间件不起作用问题

当使用 get: ^5.0.0-release-candidate-9.2.1最新版本时&#xff0c;中间件GetMiddleware各种教程都是让我们在redirect中实现&#xff0c;比如&#xff1a; overrideRouteSettings? redirect(String? route) {return RouteSettings(name: "/companyAuthIndexPage"…...

RabbitMQ(三)

RabbitMQ中的各模式及其用法 工作队列模式一、生产者代码1、封装工具类2、编写代码3、发送消息效果 二、消费者代码1、编写代码2、运行效果 发布订阅模式一、生产者代码二、消费者代码1、消费者1号2、消费者2号 三、运行效果四、小结 路由模式一、生产者代码二、消费者代码1、消…...

【Python】Python之locust压测教程+从0到1demo:基础轻量级压测实战(1)

文章目录 一、什么是Locust二、Locust 架构组成三、实战 Demo准备一个可调用的接口编写一个接口测试用例编写一个性能测试用例执行性能测试用例代码1、通过 Web UI 执行&#xff08;GUI模式&#xff09;2、通过命令行执行&#xff08;非GUI模式&#xff09; 小知识&#xff1a;…...

【JavaScript】基础内容,HTML如何引用JavaScript, JS 常用的数据类型

HTML 嵌入 Javascript 的方式 引入外部 js 文件 <head> <script Language "javaScript" src"index.js"/> </head>内部声明 <head> <script language"javascript">function hello(){alert("hello word&qu…...

vue使用自动化导入api插件unplugin-auto-import,避免频繁手动导入

‌unplugin-auto-import‌是一个现代的自动导入插件&#xff0c;旨在简化前端开发中的导入过程&#xff0c;减少手动导入的繁琐工作&#xff0c;提升开发效率。它支持多种构建工具&#xff0c;包括Vite、Webpack、Rollup和esbuild&#xff0c;并且可以与TypeScript配合使用&…...

在 C# 中的Lambda 表达式

在 C# 中&#xff0c;Lambda 表达式是用来定义匿名函数的一种简洁方式&#xff0c;通常用于简化代码&#xff0c;尤其是在 LINQ 查询、事件处理或方法作为参数的场景中。Lambda 表达式的语法如下&#xff1a; 基本语法 (parameters) > expression_or_statement_blockparam…...

奉加微PHY6230兼容性:部分手机不兼容

从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…...

32单片机综合应用案例——基于GPS的车辆追踪器(三)(内附详细代码讲解!!!)

困难不会永远存在&#xff0c;只要你勇于面对&#xff0c;坚持努力&#xff0c;就一定能够战胜一切困难。每一次挑战都是一次成长的机会&#xff0c;不要害怕失败&#xff0c;失败是成功之母。只有经历过失败&#xff0c;你才能更加明白自己的不足&#xff0c;并不断改进自己&a…...

45_Lua模块与包

Lua中的模块系统是该语言的一个重要特性,它允许开发者将代码分割成更小、更易于管理的部分。通过使用模块,你可以创建可重用的代码片段,并且可以降低代码间的耦合度。下面我将详细介绍Lua模块的基本概念、语法以及一些实际案例。 1.Lua模块 1.1 模块的基本概念 从Lua 5.1…...

深度学习电影推荐-CNN算法

文章目录 前言视频演示效果1.数据集环境配置安装教程与资源说明1.1 ML-1M 数据集概述1.1.1数据集内容1.1.2. 数据集规模1.1.3. 数据特点1.1.4. 文件格式1.1.5. 应用场景 2.模型架构3.推荐实现3.1 用户数据3.2 电影数据3.3 评分数据3.4 数据预处理3.5实现数据预处理3.6 加载数据…...

【Git 】探索 Git 的魔法——git am 与补丁文件的故事

在日常的开发协作中&#xff0c;你可能会遇到这样的场景&#xff1a;某位热心的小伙伴发来一份 .patch 文件&#xff0c;让你把某个问题修复合并到项目中。如果你不知道如何优雅地接收并应用这份补丁&#xff0c;那么这篇文章就是为你准备的&#xff01;让我们一起揭开 Git 的“…...

G1原理—5.G1垃圾回收过程之Mixed GC

大纲 1.Mixed GC混合回收是什么 2.YGC可作为Mixed GC的初始标记阶段 3.Mixed GC并发标记算法详解(一) 4.Mixed GC并发标记算法详解(二) 5.Mixed GC并发标记算法详解(三) 6.并发标记的三色标记法 7.三色标记法如何解决错标漏标问题 8.SATB如何解决错标漏标问题 9.重新梳…...

机器人传动力系统介绍

电驱动系统 无框力矩电机减速器&#xff1a;优点是功率密度高&#xff0c;可在有限空间产生大扭矩&#xff0c;使机器人关节运动有力灵活&#xff0c;如人形机器人四肢运动。缺点是系统复杂&#xff0c;成本高&#xff0c;减速器会降低传动效率.空心杯电机行星滚柱丝杆&#x…...

1161 Merging Linked Lists (25)

Given two singly linked lists L1​a1​→a2​→⋯→an−1​→an​ and L2​b1​→b2​→⋯→bm−1​→bm​. If n≥2m, you are supposed to reverse and merge the shorter one into the longer one to obtain a list like a1​→a2​→bm​→a3​→a4​→bm−1​⋯. For ex…...

内联变量(inline variables):在多个文件中共享全局常量

在 C17 中&#xff0c;引入了 内联变量&#xff08;inline variables&#xff09; 的概念&#xff0c;可以用于在多个文件中共享全局常量。内联变量允许在头文件中定义变量&#xff0c;而不会导致链接错误&#xff08;如重复定义&#xff09;。这种方式非常适合用于定义跨多个文…...

Jmeter进行http接口并发测试

目录&#xff1a; 1、Jmeter设置&#xff08;1&#xff09;设置请求并发数&#xff08;2&#xff09;设置请求地址以及参数&#xff08;3&#xff09;添加结果数 2、启动看结果 1、Jmeter设置 &#xff08;1&#xff09;设置请求并发数 &#xff08;2&#xff09;设置请求地址…...

力扣解题汇总_JAVA

文章目录 数学_简单13_罗马数字转整数66_ 加一9_回文数70_爬楼梯69_x的平方根509_斐波那契数列2235_两整数相加67_二进制求和415_字符串相加2413_最小偶倍数2469_温度转换704_二分查找(重点) 数组_简单1_两数之和88_合并两个有序数组 链表_简单21_合并两个有序链表203_移除链表…...