天天看点

3-4 面向对象的编程OOP

本次课程的目的

▪ 将抽象数据类型的接口与其实现分离,并使用Java接口类型强制分离。

▪ 用接口定义adt,并编写实现接口的类。

大纲

▪ 基本概念:对象、类、属性、方法、接口和枚举

▪ 面向对象的封装与信息隐藏的显著特征

继承与重写

多态,子类型与重载、静态与动态调度

▪ Java中一些重要的对象方法

▪ 设计好的课程、

▪ 面向对象的历史

▪ 摘要

1基本概念:对象、类、属性和方法

对象

▪ 现实世界中的物体有两个共同的特征:它们都有状态和行为。

▪ 识别现实世界对象的状态和行为是从面向对象的角度开始思考的一个好方法。

–狗有状态(名称、颜色、品种、饥饿)和行为(吠叫、抓取、摇尾巴)。

–自行车有状态(当前档位、当前踏板节拍、当前速度)和行为(换档、更改踏板节拍、踩刹车)。

▪ 对于你看到的每一个物体,问自己两个问题,这些真实世界的观察结果都转化为面向对象编程的世界:这个物体可能处于什么样的状态?

–此对象可以执行哪些可能的行为?

对象是状态和行为的集合

▪ State–对象中包含的数据。–在Java中,这些是对象的字段

▪ 行为

——对象支持的动作

——在Java中,这些被称为方法

——方法只是面向对象的函数

——调用方法=调用函数

Class

▪ 每个对象都有一个类

——一个类定义方法和字段

——方法和字段统称为成员

▪ 类同时定义类型和实现-类型≈可以使用对象的位置-实现≈对象如何做事情

▪ 松散地说,类的方法是其应用程序编程接口(API)——定义用户如何与实例交互

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

类的静态与实例变量/方法

▪ 类变量:与类关联而不是与类的实例关联的变量。您还可以将方法与类(class methods)关联。

–若要引用类变量和方法,请将类的名称、类方法或类变量的名称与句点(’.’)连接起来。

▪ 非类方法或类变量的方法和变量称为实例方法和实例变量。

–要引用实例方法和变量,必须引用类实例中的方法和变量

▪ 摘要:

–类变量和类方法与类关联,并且每个类发生一次。使用它们不需要创建对象。

–实例方法和变量在类的每个实例中出现一次。

类的静态与实例变量/方法

3-4 面向对象的编程OOP

类的静态与实例变量/方法

▪ 静态方法不与类的任何特定实例相关联,而必须对特定对象调用实例方法(声明时不使用Static关键字)。

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

2接口和枚举

接口

▪ Java的接口是设计和表达ADT的一种有用的语言机制,它的实现是实现该接口的类。

–Java中的接口是方法签名列表,但没有方法体。

–如果类在其implements子句中声明接口,则它实现接口,并为接口的所有方法提供方法体。

–一个接口可以扩展一个或多个其他接口

–一个类可以实现多个接口

-一个接口可以有多个实现类

3-4 面向对象的编程OOP

与示例类一起使用的接口

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

修改类以使用接口

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

接口允许多种实现

3-4 面向对象的编程OOP

接口将客户机与实现分离

3-4 面向对象的编程OOP

Java接口和类

▪ 接口与类-接口:指定期望

-类:按期望交付(实现)

▪ 类确实定义了类型

——公共类方法可以像接口方法那样使用

——公共字段可以直接从其他类访问

▪ 但是更喜欢使用接口

——除非你知道一个实现就足够了,否则对变量和参数使用接口类型。

–支持实现的更改;

–防止对实现细节的依赖

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

为什么要多个实现?

▪ 不同的性能—选择最适合使用的实现

▪ 不同的行为-选择您想要的实现-行为必须符合接口规范(“契约”)

▪ 通常性能和行为都会有所不同–提供功能–性能权衡–例如:HashSet、TreeSet

使用MyString及其实现

▪ 问题:打破了抽象障碍

——客户机必须知道具体表示类的名称。

–因为Java中的接口不能包含构造函数,所以它们必须直接调用一个具体类的构造函数。

–该构造函数的规范不会出现在接口中的任何地方,因此无法静态保证不同的实现甚至会提供相同的构造函数。

使用静态工厂而不是构造函数

3-4 面向对象的编程OOP

枚举

▪ 有时类型有一个小的、有限的不可变值集,例如:–一年中的几个月:一月、二月、…、十一月、十二月

–一周中的几天:星期一、星期二、…、星期六、星期日–指南针点:北、南、东、西

▪ 当值集很小且有限时,将所有值定义为命名常量(称为枚举)是有意义的。Java具有enum构造。

3-4 面向对象的编程OOP

向枚举添加数据和行为

3-4 面向对象的编程OOP

4封装和信息隐藏

信息隐藏

▪ 区分一个设计良好的模块和一个糟糕的模块的一个最重要的因素是它对其他模块隐藏内部数据和其他实现细节的程度

▪ 精心设计的代码隐藏了所有的实现细节

——清晰地将API与实现分离

——模块只通过API进行通信

——它们对彼此的内部工作不敏感

▪ 被称为信息隐藏或封装,是软件设计的基本原则。

信息隐藏的好处

▪ 分离组成系统的类-允许它们单独开发、测试、优化、使用、理解和修改

▪ 加快系统开发——类可以并行开发

▪ 减轻了维护的负担——类可以更快速地理解和调试,而不必担心会损害其他模块

▪ 实现有效的性能优化-Hot class可以单独优化

▪ 增加软件重用——松散耦合的类通常在其他上下文中被证明是有用的

带接口的信息隐藏

▪ 使用接口类型声明变量

▪ 客户端只能使用接口方法

▪ 无法从客户端代码访问字段

▪ 但这只需要我们到目前为止-客户端可以直接访问非接口成员-本质上,这是自愿的信息隐藏

成员的可见性修改器

▪ private–只能从声明类访问

▪ 受保护–可从声明类的子类(以及包内)访问

▪ 公共-可从任何地方访问

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

信息隐藏的最佳实践

▪ 仔细设计你的API

▪ 只提供客户端所需的功能,所有其他成员都应是私有的

可以在不妨碍调用者的情况下,在后期将私有变为 公有

不能进行相反操作,会破坏调用者的使用

5继承和重写

继承

▪ 继承用于代码重用-只写一次代码-在子类中隐式提供超类特性(公共的,受保护的)

3-4 面向对象的编程OOP

(1) Overriding

可重写方法和严格继承

▪ 可重写方法:允许重新实现的方法。

–在Java中,方法在默认情况下是可重写的,即没有特殊的关键字。

▪ 严格继承(Strict inheritance)

——子类只能向超类添加新方法,不能覆盖它们

——如果在Java程序中不能覆盖某个方法,则必须以关键字final作为前缀。

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

final

▪ final field:防止在初始化后重新分配到该字段

▪ final method:防止重写该方法

▪ final类:防止扩展该类

——例如,public final类CheckingAccountImpl{。。。}

覆盖(覆盖/重写)

▪ 方法重写是一种语言特性,它允许子类或子类提供方法的特定实现,该方法已由其父类之一提供。

–相同的名称、相同的参数或签名以及相同的返回类型。

–执行的方法的版本将由用于调用它的对象确定。

。如果使用父类的对象来调用该方法,则将执行父类中的版本;如果使用子类的对象来调用该方法,则将执行子类中的版本。

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

当子类包含重写超类方法的方法时,它还可以使用关键字super调用超类方法。

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

重写方法的错误使用

▪ 用全新的含义覆盖超类的操作。

3-4 面向对象的编程OOP

该重写将加法定义为了减法,减法定义为了加法

重写方法的提示

▪ 如果要重写方法:

–确保签名匹配–使用@override以便编译器支持

–复制和粘贴声明(或让IDE为您执行此操作)

–可见设置可以保持不变或增加,但不能降低。

3-4 面向对象的编程OOP

(2) 抽象类

抽象方法和抽象类

▪ 抽象方法:

–有签名但没有实现的方法(也称为抽象操作)

–由关键字Abstract定义

▪ 抽象类:

–包含至少一个抽象方法的类称为抽象类

–它不能被实例化!

–在实例化从抽象类派生的类之前,其父类的所有抽象方法都必须由派生链中的某个类实现

3-4 面向对象的编程OOP

抽象方法和抽象类

▪ 接口:只有抽象方法的抽象类,接口主要用于系统或子系统的规范。实现由子类或其他机制提供。

▪ 具体类→抽象类→接口

3-4 面向对象的编程OOP

抽象类缺少一个或多个方法的实现

受保护的元素在子类中可见

抽象方法留在子类中实现

3-4 面向对象的编程OOP

6多态、子类型和重载

(1) 三种多态性

三种多态性(多态)

▪ 特殊多态性:当一个函数表示不同的、潜在的异构实现时,取决于个别指定的类型和组合的有限范围。许多使用函数重载(function overloading)的语言都支持特殊多态性。一个方法可以有多个同名的实现(方法重载)

▪ 参数多态性(Parametric polymorphics):当编写代码时没有提到任何特定类型,因此可以透明地与任何数量的新类型一起使用。在面向对象编程社区中,这通常被称为泛型或泛型编程。一个类型名字可以代表多个类型(泛型编程)

▪ 子类型(也称为子类型多态性或包含多态性):当一个名称表示由某个公共超类关联的许多不同类的实例时

(2) 特殊多态性和重载

特殊多态性

▪ 当一个函数在几个不同的类型上工作(可能没有显示出一个公共的结构)并且可能以不相关的方式对每种类型进行操作时,就可以获得特殊的多态性

3-4 面向对象的编程OOP

重载

▪ 重载方法允许您在类中重用相同的方法名,但使用不同的参数(也可以选择使用不同的返回类型)。

▪ 重载方法通常意味着您对那些调用您的方法的人稍微好一点,因为您的代码承担了处理不同参数类型的负担,而不是在调用您的方法之前强制调用方进行转换。

重载

▪ 函数重载是使用不同的实现创建多个同名方法的能力。

–对重载函数的调用将运行该函数的特定实现,该实现适合于调用的上下文,允许一个函数调用根据上下文执行不同的任务。

▪ 重载是一种静态多态性——使用“最佳匹配技术”解析函数调用,即根据参数列表解析函数。

–函数调用中的静态类型检查–在编译时解析这些方法中使用哪种方法的确定。

在编译阶段时决定要具体执行哪个方法 (static type checking) – 与之相反,overridden methods则是在run-time进行dynamic checking

重载规则

▪ 函数重载中的规则:重载的函数必须根据arity或数据类型而有所不同

-必须更改参数列表。

–可以更改返回类型。

–可以更改访问修饰符。public/private/protected

–可以声明新的或更广泛的检查异常。

–方法可以在同一个类或子类中重载

3-4 面向对象的编程OOP

要调用的方法的哪个重载版本在运行时根据对象类型决定,但要调用的方法的哪个重载版本基于编译时传递的参数的引用类型。

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

重写与重载

▪ 不要将重写派生类(baiquity)中的方法与重载方法名混淆

–重写方法时,派生类中给出的新方法定义的参数数量和类型与基类中的完全相同

-当派生类中的方法与基类中的方法具有不同的签名(即重载)时

-请注意,当派生类重载原始方法时,它仍然继承原始方法也来自基层

3-4 面向对象的编程OOP

(3) 参数多态性与泛型编程

参数多态性

▪ 参数多态性是当一个函数在一系列类型上一致工作时获得的;这些类型通常表现出一些共同的结构。

–它能够以泛型方式定义函数和类型,以便它基于运行时传递的参数工作,即允许静态类型检查,而不完全指定类型。

参数多态性

▪ 泛型编程是一种编程方式,在这种编程方式中,数据类型和函数是按照要在以后指定的类型编写的,然后在需要时对作为参数提供的特定类型进行实例化

泛型编程的核心思想是从具体的、有效的算法中抽象出泛型算法,从而获得可以与不同数据表示相结合的泛型算法,从而生成各种有用的软件。

Java中的泛型

▪ 类型变量(type variable)是不合格(unqualified)标识符。

–它们由泛型类声明、泛型接口声明、泛型方法声明引入

▪ 一个classis泛型,如果它声明一个或多个类型变量。

–这些类型变量称为类的类型参数(baiquity)。

–它定义一个或多个用作参数的类型变量。

–泛型类声明定义一组参数化类型,每个类型参数部分的可能调用都有一个参数化类型。

–所有这些参数化类型在运行时共享同一个类。

使用菱形运算符<>,帮助声明类型变量

3-4 面向对象的编程OOP

Java中的泛型

▪ 如果接口声明一个或多个类型变量,则它是泛型的。

–这些类型变量称为接口的类型参数。

–它定义一个或多个用作参数的类型变量。

–泛型接口声明定义一组类型,每个类型参数部分的可能调用都有一个类型。

–所有参数化类型在运行时共享同一接口。

3-4 面向对象的编程OOP

另一个例子:Java集

▪ 集合是某些其他类型E的有限元集合的ADT。

▪ Set是泛型类型的一个示例:一种类型,它的规范是按照稍后要填写的占位符类型。

▪ 我们没有为Set、Set等编写单独的规范和实现,而是设计并实现了一个Set。

3-4 面向对象的编程OOP

通用接口

▪ 假设我们要实现泛型Set接口。

–方法1:通用接口,非通用实现:实现特定类型E的Set

3-4 面向对象的编程OOP

方法2:通用接口,通用实现。

–我们还可以实现泛型Set接口,而不必为E选择类型。

–在这种情况下,我们编写的代码与客户机为E选择的实际类型无关。

–Java的HashSet对Set执行此操作。

3-4 面向对象的编程OOP

Java中的泛型方法

▪ 如果方法声明一个或多个类型变量,则该方法是泛型的。

–这些类型变量称为方法的形式类型参数。

–形式类型参数列表的形式与类或接口的类型参数列表相同。

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

(4) 亚型多态性

继承和子类型:层次结构的一瞥

▪ Java集合API

3-4 面向对象的编程OOP

▪ 继承/子类型的好处:代码重用、建模灵活性

▪ 在Java中:每个类只能直接扩展一个父类;一个类可以实现多个接口。

子类型

▪ “B是a的一个子类型”表示“每个B都是a”

▪ 在规范方面:“每个B都满足A的规范。”

—如果B的规范至少和A的规范一样强,B只是A的子类型。

–当我们声明一个实现接口的类时,Java编译器会自动执行这一要求的一部分:它确保a中的每个方法都出现在B中,具有兼容的类型签名。

–B类不能在不实现A中声明的所有方法的情况下实现接口A。

子类型的静态检查

▪ 但是编译器无法检查我们是否在其他方面削弱了规范:

–加强方法的某些输入的先决条件

–削弱后决条件

–削弱接口抽象类型向客户播发的保证。

▪ 如果在Java中声明子类型(例如实现接口),则必须确保子类型的规范至少与父类型的规范一样强。

亚型多态性

▪ 子类型多态性:客户端代码可以统一处理不同类型的对象▪ 每个对象根据其类型进行操作(例如,如果添加了新类型的帐户,则客户端代码不会更改)

▪ Liskov替换原则(LSP):

—如果S是T的一个子类型,那么T类型的对象可以替换为S类型的对象(即T类型的对象可以替换为S子类型的任何对象),而不改变T的任何所需属性。

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

instanceof()

▪ 测试对象是否属于给定类的运算符

3-4 面向对象的编程OOP

▪ 建议:尽可能避免instanceof(),永远不要(?)在超类中使用instanceof()检查子类的类型

▪ 有时候你想要一个不同的类型

double pi = 3.14; int indianaPi = (int) pi;

▪ 如果您知道您有一个更具体的子类型,则很有用:

Account acct = …; CheckingAccount checkingAcct = (CheckingAccount) acct; long fee = checkingAcct.getFee();

▪ 但如果类型不兼容,它将获得ClassCastException▪ 建议:避免贬低类型为什么?–从不(?)在超类中向下转换为子类的原因

Java中的一些重要对象方法

重写对象方法

▪ equals()–如果两个对象“相等”,则为true

▪ hash code()–用于哈希映射的哈希代码

▪ toString()–可打印的字符串表示

▪ toString()——丑陋且不具信息性

——你知道你的对象是什么,所以你可以做得更好

——总是重写toString(),除非你知道不会调用该方法

▪ equals&hashCode–标识语义

–如果需要比值,则必须重写,否则不需要

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

替代哈希码重写

▪ 效率较低,但在其他方面同样好!

3-4 面向对象的编程OOP

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200401104031186.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzc4NDUzNw==,size_16,color_FFFFFF,t_70

3-4 面向对象的编程OOP

▪ Name重写了hashCode

,但没重写Equals!因此,这两个名称实例是不相等的。

3-4 面向对象的编程OOP

11设计好的class

不可变类的优点

▪ 简单

▪ 固有的线程安全性

▪ 可以自由分享

▪ 不需要防御副本

▪ 优秀的building blocks

如何编写不可变类

▪ 不提供任何突变物

▪ 确保不能重写任何方法

▪ 使所有字段成为最终字段

▪ 将所有字段设为私有

▪ 确保任何可变组件的安全性(避免重复暴露)

▪ 实现toString()、hashCode()、clone()、equals()等

3-4 面向对象的编程OOP
3-4 面向对象的编程OOP

何时使类不可变

▪ 总是,除非有充分的理由不

▪ 总是让小的“值类”不可变!–例如:颜色、电话号码、单位

何时使类可变

▪ 类表示其状态更改的实体-现实世界-银行帐户、TrafficLight-抽象-迭代器、匹配器、集合-进程类-线程、计时器

▪ 如果类必须是可变的,则最小化可变性-

构造函数应完全初始化实例

-避免重新初始化方法

12面向对象编程的历史

3-4 面向对象的编程OOP

摘要总结

▪ 物体定向标准

▪ 基本概念:对象、类、属性、方法和接口

▪ 面向对象封装和信息隐藏的显著特点继承和重写多态性、子类型和重载静态和动态调度

▪ Java中一些重要的对象方法

▪ 编写不可变类

▪ 面向对象的历史

继续阅读