HIT软件构造课程复习笔记(三)

3.4 面向对象编程

OOP的基本概念

类成员变量/类方法直接与类相关联而非类的实例化对象,对应的与实例关联的称之为实例成员变量/实例方法。静态/类方法只需要在内存中共享一块内存即可。

OOP的不同特性

接口

Interface和Class: 定义和实现ADT
接口之间可以继承与扩展
一个类可以实现多个接口(从而具备了多个接口中的方法)
一个接口可以有多种实现类

接口:确定ADT规约;类:实现ADT
也可以不需要接口直接使用类作为ADT,既有ADT定义也有ADT实现
实际中更倾向于使用接口来定义变量
这样打破了抽象边界,接口定义中没有包含constructor,也无法保证所有实现类中都包含了同样名字的constructor。 故客户端需要知道该接口的某个具体实现类的名字,我们推荐使用工厂方法来提供实例对象

继承和重写

可重写方法:允许重新实现的方法
严格继承:子类只能添加新方法,无法重写父类中的方法(final限定)

重写:方法重写是一种语言特性,它允许子类以特定的方式实现父类的方法,方法的参数和返回值必须与父类中的方法一致,实际执行时调用哪个方法,运行时决定;如果使用父类的对象调用该方法,则执行父类中的版本,如果子类的对象被用来调用方法,那么子类中的版本将被执行。

父类型中的被重写函数体不为空,意味着对其大多数子类型来说,该方法是可以被直接复用的,而对某些子类型来说,有特殊性,故重写父类型中的函数,实现自己的特殊要求;如果父类型中的某个函数实现体为空, 意味着其所有子类型都需要这个功能, 但各有差异,没有共性,在每个子类中均需要重写。子类可以调用super()使用父类方法的功能,重写的时候,不要改变原方法的本意。

抽象类:
抽象方法:有方法的特性但没有实现体的方法
抽象类:包含至少一个抽象方法的类
接口:只有抽象方法的抽象类
如果某些操作是所有子类型都共有,但彼此有差别,可以在父类型中设计 抽象方法,在各子类型中重写;所有子类型完全相同的操作,放在父类型中实现,子类型中无需重写。有些子类型有而其他子类型无的操作,不要在父类型中定义和实现,而应在特定子类型中实现。
protected修饰的属性在子类中可以访问

多态、子类型、重载

三种多态:

特殊多态:当一个函数表示不同的、可能异构的实现时,具体实现取决于有限的单独指定的类型和组合,特殊多态在许多语言中使用功能重载。
参数化多态:当代码在编写时没有提到任何特定类型,因此可以对任意数量的新类型透明地使用。在面向对象编程社区中,这通常称为泛型或泛型编程。
子类型多态、包含多态:一个名称表示由某个通用父类关联的许多不同类的实例。

特殊多态:当一个函数用几个不同的类型(这些类型可能没有共同的结构)实现,并且可能以与每种类型不相关的方式运行时,就可以获得特殊多态性。

重载:多个方法具有同样的名字,但有不同的参数列表或返回值类型。这样方便client调用,client可用不同的参数列表,调用同样的函数。重载是一种静态多态,根据参数列表进行最佳匹配和静态类型检查,在编译阶段决定要执行哪个方法。与之相对应的是重写,它进行动态类型检查。
重载方法参数列表必须不同,其他可相同可不同,可以在同一个类内重载,也可在子类中重载。

(重载静态看前面,重写动态看后面)

参数化多态:当一个方法一致地在一个类型范围上运行时得到的参数化多态。这些类型通常表现出一些共同的结构,它有能力以通用的方式定义方法和类型,这样就能在运行时传递参数,并允许在不完全指定类型的情况下进行静态类型转换,我们将其称为泛型。
泛型编程是一种编程风格,其中数据类型和方法按照要指定的类型编写——之后在需要时实例化时参数提供再特定类型,这样使得程序更具有泛用性。

例如Java中的Set类,它是一组其他类型的元素组成的集合,定义的方法都使用了泛型。

泛型接口,可以有非泛型的实现类,也可以搭配使用泛型实现类。
通配符?只能在使用泛型的时候出现,不能在定义中出现:
List<?> list = new ArrayList();
List<? extends Animal>
List<? super Animal>
泛型在运行时会被擦除,且不存在泛型数组(编译不通过)
Pair[] foo = new Pair[42];

子类型多态:
继承:每个类只能继承一个父类,但可以实现多个接口。“B是A的子类”意味着“每个B都是A,B满足A的规约”,并且B要实现A中声明过的每个方法,子类型的规约不能弱化父类的规约。
子类型多态:不同类型的对象可以统一的处理而无需区分,每个对象依据自己的实际类型进行不同的行为,从而隔离了变化。

LSP灵活可替换原则:如果S是T的子类型,那么类型T的对象可以替换为类型S的对象(即类型T的对象可以替换为类型S的任何对象),而不改变T的任何期望属性。

instanceof关键字可以检测前者是否为后者类。
注意:不要把一个父类对象强转成子类,会报错

Java中重要的Object方法

equals():
传入一个Object对象,用instanceof动态检查是否为比较对象的类型,不是就返回false;否则将传入对象强转,逐一比较对象的各个属性,都相等再返回true

hashCode():
不同类型属性的重写方法:
@Override
public int hashCode() {
int result = 17;
result = 37 * result + name.hashCode();
result = 37 * result + age;
result = 37 * result + (int)(friendNumber^(friendNumber>>32));
result = 37 * result + Float.floatToIntBits(cash);
result = 37 * result + (int)(Double.doubleToLongBits(wealth)^(Double.doubleToLongBits(wealth)>>32));
result = 37 * result + (isMarry ? 1 : 0);
return result;
}

toString()

设计一个好的类

不可变类型类的优势:
简单/线程安全/自由分享/无需防御式拷贝
构建方法:
不提供变值器,确保方法不会被重写,所有属性设置为private、final,防止表示泄露,重写equals、hashCode、toString等方法。
能写成不可变类就写,需要可变类型就把可变性减到最小。

3.5 ADT和OOP中的等价性

不可变类型的相等

等价关系:自反、对称、传递
AF映射到同样的结果,则等价,故AF是一个到等价类的映射
观察等价性:站在外部观察者角度,对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。 反之亦然。

==和equals()

引用等价性:==比较两个引用是否指向同一块内存。Snapshot图中则是两个箭头指向同一个对象圈
对象等价性:通过调用equals()方法进行比较

在自定义ADT时,需要重写Object的equals()
基本数据类型使用==比较,对象类型使用equals() ,如果用 ==则是在判断两个对象身份标识ID是否相等(指向内存里的同一段空间)

实现equals()

在Object中实现的缺省equals()是在判断引用等价性,这不是我们所期望的,因此需要重写。
传入非Object对象的参数会被认为是重载,因此要加@Override检查

注意:永远不要在父类中使用instanceof来检查子类的类型

使用多态来避免使用instanceof的情况

Object的规约

“相等”的对象,其hashCode()的结果必须一致,除非对象被修改了,否则调用多次equals应是同样的结果。equals方法体现的是两个对象满足等价关系——自反、对称、传递。

Hash table:哈希表提供常量时间查找,因此它们往往比树或列表执行得更好。键值对中的键不必是有序的,除了提供equals和hashCode之外不必有任何特定的属性。经过对键的哈希值的计算,我们将其传递给数组范围内的一个索引,对应的值稍后将插入。哈希表的表示不变量包括一个基本约束,即键位于哈希值所决定的槽中。

等价的对象必须有相同的hashCode;不相等的对象,也可以映射为同样的hashCode,但性能会变差。equals方法判定相等的对象如果哈希值不同,其值将会被放入不同槽中,可能会导致查找失败。总之,重写equals方法时一定要重写hashCode方法,除非你能保证你的ADT不会被放入到Hash类型的集合类中。

可变类型的相等

观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致
行为等价性:调用对象的任何方法都展示出一致的结果

对可变类型来说,往往倾向于实现严格的观察等价性。但在有些时候,观察等价性可能导致bug,甚至可能破坏RI
可变对象在发生改变时,其哈希值也会随之改变,自身将被放入另一个哈希桶中,这可能会导致一些查找操作的失败。因此,如果某个可变对象包含在Set集合类中,当其发生改变后,集合类的行为将变得不确定。

对可变类型,实现行为等价性即可,也就是说,只有指向同样内存空间的对象才是相等的。所以对可变类型来说,无需重写这两个函数,直接继承Object的两个方法即可,如果一定要判断两个可变对象看起来是否一致,最好定义一个新的方法。而Collections类没有遵守这个原则,采用了观察等价性。

自动封装和相等

基本类型的包装类对象如果值相等,则equals方法返回true,但==比较的是引用,返回false。如果进行强转回基本类型再用 ==比较,则返回true
由于常量池的存在,如int-128到127会产生不同的结果,即返回true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值