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

7.继承与多态 对象村的优质生活

7.1 民法亲属篇:继承(inheritance)

了解继承

在设计继承时,你会把共同的程序代码放在某个类中,然后告诉其他的类说此类是它们的父类。当某个类继承另一个类的时候,也就是子类继承自父类。以Java的方式说,这是“子类继承父类”。继承的关系意味着子类继承了父类的方法。当我们提及“类的成员”时,成员的意思就是实例变量和方法。

举例来说,如果PantherMan是个SuperHero的子类,则PantherMan会自动地继承SuperHero的实例变量和方法,包括了suit、tights、specialPower、useSpecialPower()等。但PantherMan可以加入自己的方法和实例变量,也可以覆盖掉继承自SuperHero的方法。

FriedEggMan不需要任何独特的行为,所以它没有覆盖过任何的方法。

然而PantherMan认为它的特殊超能力需要特别处理过的方法,所以就覆盖掉useSpecialPower()与putOnSuitO。

实例变量无法被覆盖掉是因为不需要,它们并没有定义特殊的行为。PantherMan可以将继承下来的tights设定成紫色,而FriedEggMan可以自行选择白色。 

继承的范例

public class Doctor {boolean worksAtHospital;void treatPatient() {//执行检查}
}
public class FamilyDoctor extends Doctor{boolean makesHouseCalls;void giveAdvice() {//提出诊断}
}
public class Surgeon extends Doctor{void treatPatient() {//进行手术}void makeIncision() {//截肢(好恶心!)}
}

 

7.2 设计继承层次

设计动物仿真程序的继承树

假设你要设计一个仿真系统程序,可以让用户设定将一群动物丢到某种环境中以观察会发生什么事情。现在不需要写出程序,我们只在乎设计。

我们已经被告知一部分会用到的动物,但是并不知道还有多少种动物会加进来。每个动物都会用一个对象来表示,且动物会在环境中活动,执行任何被设计出的行为。

这个程序必须能够在任何时间加入新类型的动物。

我们首先要辨别出所有动物都有的、抽象的共同特征,然后以这些共同特征设计出能够让所有动物加以扩充的类。
1.找出具有共同属性和行为的对象

这6种动物有什么共同点?这么文可以帮助我们执行第二个步骤。这些类型有什么相关性?这么问有助于这些第四到第五个步骤

7.3 运用继承避免重复的程序代码

运用继承来防止子类中出现重复的程序代码

我们有5个实例变量:
picture:动物JPEG图像的名称。

food:此动物所吃的食物。现在只有meat和grass两种值。

hunger:代表饥饿程度的int值。它会根据动物吃了多少东西而改变。

boundaries:代表动物活动范围区域的长宽。

location:动物在活动区域中的X与Y坐标。

还有4个方法:
makeNoise():动物发出声音的行为程序。
eat():动物遇到食物时的行为程序。
sleep():睡眠的行为程序。
roam():不在进食或睡眠时的行为程序。

2.设计代表共同状态与行为的类

这些对象都是动物,因此可以用Animal作为共同父类的名称

会把所有动物都需要的方法和实例变量加进去

7.4 方法的覆盖(override)

假设我们都同意一件事:所有Animal类型上的实例变量都合用。狮子的picture带有它的图片路径food的值是meat。猫的图片就是猫、猫的食物是meat(其实有养猫的人都知道猫也会吃草)。所以实例变量没有问题,但是行为程序呢?

我们应该覆盖哪些方法呢?

狮子的叫声会跟河马一样吗?也许你认为一样,但是我们会根据类型设计出不同的行为程序。当然,我们也可以用实例变量来存放声音文件的路径值,而让makeNoise()方法都执行相同的动作。但是有时候行为的复杂程度不只是如此而已。

因此就跟阿米巴虫覆盖过rotate()这个方法的例子一样,我们会让某些行为使用各个类自行指定的程序,而不是使用共同的程序。

3.决定子类是否需要让某项行为(也就是方法的实现)有特定不同的运作方式

观察Animal这个类之后,认为eat()与makeNoise()应该由各个子类自行覆盖

4.通过寻找使用共同行为的子类来找出更多抽象化的机会 

观察到Wolf与Dog可以由某些共同的行为,在Lion、Tiger、Cat之间也是

 5.完成类的继承层次

因为动物本来就有组织化的层次(界、门、纲、目、科、属、种),我们可以用这些层次来制作有意义的类设计。我们使用犬科和猫科动物的分类来作出Feline与Canine这两个类。

我们决定Canine使用共同的roam()方法,因为它们都以相同的方式移动。Feline之间也是差不多。而Hippo则持续使用继承下来的roam()方法。

所以这个层次就这么完成了,稍后我们会再回到这个部分。

调用哪个方法?
Wolf这个类有4个方法。其中一个继承自Animal,一个来自Canine(实际上也是覆盖过Animal的方法),还有两个是自己覆盖过的。当你创建出一个Wolf对象并赋给它变量时,你可以使用圆点运算符来调用变量所引用对象的方法。但是这会调用哪个版本的方法呢? 

当你调用对象引用的方法时,你会调用到与该对象类型最接近的方法。换句话说,最低阶的会胜出!“最低阶”的意思是在层次树的最下方。Canine比Animal低,而Wolf是在Canine的下方,因此Java虚拟机会从Wolf开始找起。如果Java虚拟机找不到Wolf版的方法,它会往上寻找直到找到为止。 

7.5 美人出浴图

“是一个”与“有一个”

当一个类继承自另外一个类时,我们会说这是子类去继承父类。若你想要知道某物是否应该要继承另一物时,则可以用IS-A测试来检验。
三角形是一个多边型……嗯,没错。
外科医生是一个医生……OK。
哈啰凯蒂是一个猫……算是吧。
澡盆是一个浴室……失败!
大肠没有洗干净...…失败中的失败!
要确认你的设计是否正确,使用这样的测试来加以检验。如果不合理,表示你的设计有问题。
浴室与澡盆确实有关联,但不是继承上的关系。浴室与澡盆发生的是HAS-A的关系。如果“浴室有一个澡盆”成立的话,这表示浴室带有澡盆的实例变量。也就是说浴室会有个澡盆的引用,但是浴室并没有继承过澡盆。

等一下!还有!

IS-A测试适用在继承层次的任何地方。如果你的继承层次树设计得很好,那么所有的子类都应该通过任一个上层父类的IS-A测试。如果类Y是继承类X,且类Y是类Z的父类,那么Z应该能通过IS-A X的测试。 

就像此处所显示出的继承树,你一定可以说:“Wolf继承Animal”或“Wolf IS-A Animal”。只要Animal位于Wolf之上,Wolf IS-AAnimal就一定会成立。
这张Animal继承图说明了:
“Wolf是一个Canine,因此Canine能做的事情Wolf都能做,而Wolf也是个Animal,所以Animal能做的事情Wolf也都能做”。
就算Wolf已经覆盖掉某些来自Animal或Canine的方法也一样。对其他的程序来说,它们只要知道Wolf能够执行这4个方法就行。至于它是怎么做的,或者它是覆盖过哪个类的,则一点都不重要。至少有一件事情可以确定,Wolf一定可以makeNoise()、eat()、sleep()和roam()。

如何知道继承设计是对的?

虽然如此,我们还是要提出IS-A测试这个建议。如果“X IS-A Y”合理,则这两者或许存在于同
一个继承结构下。也有可能两者根本是相同的,或者刚好有相同的行为。
注意:继承概念下的IS-A是个单向的关系!
“三角形是一个多边形”这是合理的,所以你可以从多边形中扩充出三角形。
但是反过来说“多边形是一个三角形”并不合理,所以多边形并不是从三角形中extend出来的。要记得X IS-A Y隐喻着X可以做出任何Y可以做的事情(且还可能会做出更多的行为)。

7.6 民法亲属篇:遗产

子类可以继承父类的成员,包括实例变量和方法。父类可以通过存取权限决定子类是否能够继承某些特定的成员。

本书中,会讨论4中存取权限,左边是最受限制的,而越往右边限制程度越小:

存取权限(access level)控制了谁可以接触什么,这对编写出坚固、设计良好的Java程序来说是很重要的。现在我们先看看public与private两项:

  • public类型的成员会被继承
  • private类型的成员不会被继承

当子类把成员继承下来时会把它们当作是自己定义的一样。例如说当某个形状体继承Shape时,就会有rotate()与playSound()这两个方法。
任一类的成员包含有自己定义出的变量和方法再加上从父类所继承下来的任何东西。

  • 当某个类会比其父类更具有特定意义时使用继承。例如说美国短毛猫是一种特定品种的猫,所以从猫中扩充出美国短毛猫是很合理的。
  • 在行为程序(实现程序代码)应该被多个相同基本类型类所共享时,应该要考虑使用继承。举例来说,方形、圆形、三角形都需要旋转和播放声音,因此将这些功能放在它们的父类上面是很合理的,并且这样也比较好维护和扩充。然而,要注意到虽然继承是面向对象程序设计的一项关键特征,但却不一定是达成重用行为程序的最佳方式。我们会教你如何运用继承,这通常也是不错的选择,但有时常用的“设计模式(design pattern)”也会提出更微妙且更有适应性的选择。
  • 若两者间的关系对于继承结构来说并不合理,则不要只是因为打算要重用其他类的程序代码而运用继承。例如,在设计钢琴对象时,不能因为想要借用河马对象的发声程序就让这两个八竿子打不着的对象产生继承上的关系。这完全不合理!(应该要创建出发音对象,然后让钢琴与河马都用HAS-A关系来运用此对象才对)。
  • 如果两者间不能通过IS-A测试就不要应用继承关系。一定要确定子类是父类一种更特定的类型才可以。

7.7 继承的好处

1.避免了重复的程序代码

在单一的位置定义共同程序代码,然后让子类继承父类的程序代码。当你想要改变这个行为程序时,只需修改这个地方,而子类就会发生同样的改变

2.定义出共同的协议

继承让你可以确保某个父类之下的所有类都会有父型所持有的全部方法(全部可继承的方法,public类型的方法),通过继承来定义相关类间的共同协议

7.8 多态(polymorphism)

当你定义出一组类的父型时,你可以用子型的任何类来填补任何需要或期待父型的位置

在多态下,引用和对象可以是不同的类型

Animal myDog = new Dog();

运用多态时,引用类型可以是实际大小类型的父类 

当你声明一个引用变量时,任何对该引用变量类型可通过IS-A测试的对象都可以被赋值给该引用。换句话说,任何extends过声明引用变量类型的对象都可以被赋值给这个引用变量。这样子你就可以做出多态数组这一类的东西。 

参数和返回类型也可以多态 

声明一个父类的引用变量,可以将子类对象赋给它

7.9 覆盖的规则

方法就是合约的标志

参数必须要一样,且返回类型必须要兼容

父类的合约定义出其他的程序代码要如何来使用方法。不管父类使用了哪种参数,覆盖此方法的子类也一定要使用相同的参数。而不论父类声明的返回类型是什么,子类必须要声明返回一样的类型或该类型的子类。要记得,子类对象得保证能够执行父类的一切。

不能降低方法的权限

这代表存取权必须相同,或者更为开放。举例来说,你不能覆盖掉一个公有的方法并将它标记为私有。这会让它以为在编译期通过的是个公有,然后突然在执行期才被Java虚拟机阻止存取。目前为止我们看过了private与public这两种存取权限。

7.10 重载(overload)

方法的重载

重载的意义是两个方法的名称相同,但参数不同。所以,重载与多态毫无关系。

重载可以有同一方法的多个不同参数版本以方便调用。比如,如果某个方法需要int,调用方就得将 double转换成int然后才能调用。若你有个重载版本取用double参数,则这样对调用方来说就简单多了。

因为重载方法不是用来满足定义在父类的多态合约,所以重载的方法比较有扩展性。

返回类型可以不同

可以任意地改变重载方法的返回类型,只要所有的覆盖使用不同的参数即可

不能只改变返回类型

如果只有返回类型不同,但参数一样,这是不允许的。编译器不会让这样的事情过关。就算是重载,也要让返回类型是父类版返回类型的子类。重载的条件是要使用不同的参数,此时返回类型可以自由地定义。

可以更改存取权限

可以任意地设定overload版method的存取权限。

重载版的方法只是刚好由相同名字的不同方法,与继承或多态无关。重载的方法与覆盖方法不一样

重载的合法案例

public class Overloads {String uniqueID;public int addNums(int a, int b) {return a + b;}public double addNums(double a, double b) {return a + b;}public void setUniqueID(String theID) {//lots of validation code, and then:uniqueID = theID;}public void setUniqueID(int ssNumber) {String numString = "" + ssNumber;setUniqueID(numString);}
}

相关文章:

7.继承与多态 对象村的优质生活

7.1 民法亲属篇:继承(inheritance) 了解继承 在设计继承时,你会把共同的程序代码放在某个类中,然后告诉其他的类说此类是它们的父类。当某个类继承另一个类的时候,也就是子类继承自父类。以Java的方式说&…...

机器视觉、图像处理和计算机视觉:概念和区别

机器视觉、图像处理和计算机视觉:概念和区别 机器视觉、图像处理和计算机视觉是相关但有区别的概念。 机器视觉主要应用于工业领域,涉及图像感知、图像处理、控制理论和软硬件的结合,旨在实现高效的运动控制或实时操作。 图像处理是指利用…...

从零开始的C语言学习第二十课:数据在内存中的存储

目录 1. 整数在内存中的存储 2. 大小端字节序和字节序判断 2.1 什么是大小端? 2.2 为什么有大小端? 3. 浮点数在内存中的存储 3.1 浮点数存的过程 3.2 浮点数取的过程 1. 整数在内存中的存储 在讲解操作符的时候,我们就讲过了下⾯的内容&#x…...

分布式内存计算Spark环境部署与分布式内存计算Flink环境部署

目录 分布式内存计算Spark环境部署 1. 简介 2. 安装 2.1【node1执行】下载并解压 2.2【node1执行】修改配置文件名称 2.3【node1执行】修改配置文件,spark-env.sh 2.4 【node1执行】修改配置文件,slaves 2.5【node1执行】分发 2.6【node2、no…...

am权限系统对接笔记

文章目录 角色如何对应机构如何对应 am需要提供的接口机构、角色、人员查关系 消息的交互方式方式1 接口查询方式2 mq推送消息到业务系统 am是一套通用权限管理系统。 为什么要接入am呢? 举例,甲方有10个供方,每个供方都有单独的权限系统,不…...

回首往昔,初学编程那会写过的两段愚蠢代码

一、关于判断两个整数是否能整除的GW BASIC创意代码 记得上大学时第一个编程语言是BASIC,当时Visual Basic还没出世,QBASIC虽然已经在1991年随MS-DOS5.0推出了,但我们使用的还是 GW-BASIC, 使用的教材是谭浩强、田淑清编著的《BA…...

《Java面向对象程序设计》学习笔记——Java程序填空题

​笔记汇总:《Java面向对象程序设计》学习笔记 这些题其实都非常滴简单,相信大伙能够立刻就秒了吧😎 文章目录 题目答案 题目 以下程序要求从键盘输入一个整数, 判别该整数为几位数, 并且输出结果, 请将下…...

Chrome跨域访问网络请求Cookies丢失的解决办法

为了保障网络安全,Chrome对跨域访问有一定的限制。一般分为三级: cookies带有“SameSite=Strict”时,只允许访问同一个域名下的网络请求;cookies带有“SameSite=Lax”时,允许访问同一个域名下的网络请求和同一个根域名下的网络请求;cookies带有“SameSite=None”时,允许…...

从创业者的角度告诉你AI问答机器人网页的重要性

在数字化时代,创业者面临着越来越多的挑战。而AI问答机器人网页正成为创业者们的必备工具。它可以提供即时客户支持、降低运营成本,并实现全天候服务。接下来,我将从创业者的角度阐述一下,AI问答机器人网页为什么那么重要&#xf…...

大数据Flink(九十七):EXPLAIN、USE和SHOW 子句

文章目录 EXPLAIN、USE和SHOW 子句 一、EXPLAIN 子句 二、USE 子句...

浏览器中的网络钓鱼防护

网络钓鱼防护是一项功能,可保护用户免受旨在窃取其敏感信息的网络钓鱼攻击,网络钓鱼是网络犯罪分子常用的技术,这是一种社会工程攻击,诱使用户单击指向受感染网页的恶意链接,用户在该网页中感染了恶意软件或其敏感信息…...

每日温度00

题目链接 每日温度 题目描述 注意点 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替1 < temperatures.length < 100000 解答思路 使用单调栈解决本题&#xff0c;思路为&#xff1a…...

【JVM】JVM的垃圾回收机制

JVM的垃圾回收机制 对象死亡判断方法引用计数算法可达性分析算法 垃圾回收算法标记清除法复制算法标记整理算法分代算法 Java运行时内存的各个区域,对于程序计数器,虚拟机栈,本地方法栈这三个部分区域而言,其生命周期与相关线程有关,随线程而生,随线程而灭,并且这三个区域的内存…...

28栈与队列-单调队列

目录 LeetCode之路——239. 滑动窗口最大值 解法一&#xff1a;暴力破解 解法二&#xff1a;单调队列 LeetCode之路——239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k …...

qt软件崩溃的分析方法-定位源文件和行号

软件&#xff08;debug版exe或者release版exe&#xff09;在正常运行状态下&#xff08;不是gdb调试运行&#xff09;&#xff0c;如果软件崩掉&#xff0c;那么会直接闪退&#xff0c;软件什么也做不了&#xff0c;此时无法保存软件中的状态信息&#xff0c;此外&#xff0c;也…...

《实验细节》上手使用PEFT库方法和常见出错问题

《实验细节》上手使用PEFT库方法和常见出错问题 安装问题常用命令使用方法保存peft模型加载本地 peft 模型使用问题问题1 ValueError: Please specify target_modules in peft_config安装问题 首先给出用到的网站 更新NVIDIA网站https://www.nvidia.com/Download/index.aspx 2…...

软考高级系统架构论文 注意事项

目录 前言正文 前言 论文主要体现 分析问题的能力以及解决问题的能力 正文 论文必要的点&#xff1a; 虚构情节、文章中有较严重的不真实或者不可信的内容出现的论文;没有项目开发的实际经验、通篇都是浅层次纯理论的论文;所讨论的内容与方法过于陈|旧&#xff0c;或者项目…...

Reasoning with Language Model Prompting: A Survey

本文是LLM系列的文章&#xff0c;针对《Reasoning with Language Model Prompting: A Survey》的翻译。 语言模型提示推理&#xff1a;综述 摘要1 引言2 前言3 方法分类4 比较和讨论5 基准与资源6 未来方向7 结论与视角 摘要 推理作为解决复杂问题的基本能力&#xff0c;可以…...

jenkins pipeline使用

1、jenkins全局配置 1.1、maven配置 1.2、jdk配置 1.3、git配置 2、构建环境配置 2.1、安装时间插件 Date Parameter 2.2、Git Parameter 插件安装 3、pipeline如下 pipeline {agent anyenvironment {image_name "192.168.122.150/ken-test/price-service:${date}&…...

MATLAB——神经网络参考代码

欢迎关注“电击小子程高兴的MATLAB小屋” %% I. 清空环境变量 clear all clc %% II. 训练集/测试集产生 %% % 1. 导入数据 load spectra_data.mat %% % 2. 随机产生训练集和测试集 temp randperm(size(NIR,1)); %打乱60个样本排序 % 训练集——50个样本 P_train NIR(…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...