Chapter3-抽象数据类型(ADT)和面向对象编程(OOP)

Chapter3

本章主要介绍:
软件构造的理论基础-ADT
软件构造的技术基础-OOP

3.1 Data Type and Type Checking

第一节研究数据类型及其特性:
基本数据类型、对象数据类型
静态类型检查、动态类型检查
Mutable、Immutable数据类型(可变数据类型的危险性,不变数据类型的优越性)
值的改变、引用的改变
防御式拷贝
Snapshot diagram理解数据类型
Null references(空指针)

3.1.1 Date type

Date type in Java(基本、对象数据类型)

primitive types(基本数据类型)object types(对象数据类型)
int(范围[-2^31,+2^31]),long(范围[-2^63,+2^63]), byte, short, char, float, double, booleanClasses, interfaces, arrays, enums, annotations, String, Integer, Double
No identity except their value(只有值, 没有ID, 与其他值无法区分)Have identity distinct from value(既有值, 也有ID)
Immutable(基本数据类型是不可变的)Some mutable, some not(可变、不可变)
On stack, exist only when in use(在栈内分配内存,代价低;函数返回后, 栈回收, 变量将不存在)On heap, garbage collected(在堆中分配内存,代价昂贵)

这里,关于对象数据类型,有几点需要注意:
①所有的非基本数据类型都是对象,Object是最上层父类,其余对象类型都是继承Object(extends);
②可以将基本数据类型包装成对象数据类型,通常都是在定义集合类型的时候使用它们(Boolean,Integer,Short,Long,Float,eg. Map

3.1.2 Mutability and Immutability

Diff of changing a variable or its value(值的改变、引用的改变)

changing a variable(改变一个变量-引用)changing a value(改变一个变量的值)
changing where the variable’s arrow points or point it to a different value(将该变量指向另一个存储空间)changing reference inside that value, such as an array or list(将该变量当前指向的值的存储空间中写入一个新的值)

Difference Of Mutability & Immutability(可变、不可变)

TypeMutability(可变性)/td> Immutability(不变性-重要的设计原则)
Introduce拥有方法可以修改自己的值、引用一旦创建,始终指向同一个值、引用;尽量使用`final`变量(如果改变final类型变量,编译器会提示错误,这也是一种静态检查)
Advantage最小化拷贝以提高效率;可以获得更好的性能;适合在多个模块之间共享数据更安全,在其他质量指标上表现更好
Risks传递或者返回一个可变类型数据, 会存在多个引用, 之后在一个地方改变这个值,其他地方也会改变频繁修改会产生大量的临时拷贝(需要垃圾回收)
ExampleDate(java.time), List(ArrayList, LinkedList), Set, Map(HashMap), StringBuilder;安全的使用可变类型:局部变量,不涉及共享;只有一个引用;使用防御式拷贝,返回一个全新的对象基本类型及其封装对象类型(eg.int -> Interger)是不可变的, String也是不可变的;可以使用包装器,`List listCopy = Collections.unmodifiableList(list)`,此后,listCopy不能再修改,否则会报错`UnsupportedOperationException`

About Immutability

Using immutable objects and immutable references as much as possible
尽可能多的使用不可变对象和不可变类型

finalTo make a reference immutable, declare it with the keyword **final**(Re. final类无法派生子类; final变量无法改变值/引用; final方法无法被子类重写)
StringString is an immutable type(一旦创建一个String类型变量, 如果需要改变值, 则需要指向另一存储空间, 原存储空间被丢弃), while StringBuilder is a mutable type(创建一个StringBuild类型变量后, 改变值是直接在原存储空间中改变)
Defensive copyReturn a new copy of the object(通过防御式拷贝, 返回一个全新的Object对象) just for mutability, while immutable types never need to be defensively copied(因为不可变类型, 一经创建, 无法改变值, 所以不用担心在不同的引用中被改变, 如果某个变量需要改变, 则要重新创建一个对象, 会有新的引用, 不用担心改变其他变量的引用)
3.1.3 Snapshot diagram

Run as a code-level, run-time, and moment view
用于描述程序运行时的内部状态,便于刻画各类变量随时间变化

①Primitive values(基本类型的值)
指向一个值
这里写图片描述

②Object values(对象类型的值)
指向一片存储空间
简单表示的是指向一个标记类型的圆圈;
详细点,内部箭头指向内部变量具体的值;
更详细,写出内部变量的声明类型;
图片名称

③Immutable values and mutable values
Immutable values - String
不可变的对象,用双线椭圆
直接改变引用, 指向新的存储空间, 丢弃之前的存储空间

mutable values - StringBuilder
直接改变存储空间中的值
图片名称

④immutable reference
不可变的引用, 用双线箭头
(引用是不可变的, 但指向的值却可以是可变的, 同样的, 可变的引用, 也可指向不可变的值,主要是对象内部可以改变)
这里写图片描述

⑤List
List is an interface, members in a List must be an object.
这里写图片描述

⑥Set
A Set is an unordered collection of zero or more unique objects.(Set-集合, 其中的元素是没有顺序的)
这里写图片描述

⑦Map
A Map is similar to a dictionary (key-value)
图片名称

⑧关于迭代器Iterator
Iterator是一种mutable类型;
Problem:通过遍历(for-each,for循环)无法成功删除list中的元素;需要使用迭代器,然后使用迭代的删除操作进行删除;下面举个简单的例子

List<String> list = new ArrayList<>();
list.add("6.01");
list.add("6.02");
list.add("6.03");
Iterator it = list.iterator()
while(it.hasnext){
    String object= it.next();
    if(object.startsWith("6.")){
        it.remove(); 
    }
}

执行如上代码之后,list为[];
如果将it.remove()改成list.remove(object),则list为[“6.02”],下面就这个问题进行解释一下:
首先list中下标0执行”6.01”,下标1执行”6.02”,下标2执行”6.03”;循环第一遍,将下标0与”6.01”断开,此时下标1指向”6.02”,下标1指向”6.03”,下标2为空;循环第二遍,将下标1与”6.03”断开,此时,遍历结束。
当然,如果使用for-each遍历,并且同样执行删除,会报错ConcurrentModificationException

3.2 Designing Specification

第二节研究方法和操作的规约及其特性:
方法的规约(specification)
前置、后置条件(pre/post-condition)
行为等价性
规约的强度及其比较

3.2.1 About Specifications

Introduce

程序和客户端之间达成的一致,Spec给供需双方都确定了责任,在调用的时候双方都要遵守;
Decoupled(解耦), 不需要了解具体实现, 不关心是什么对象, 只需要知道有什么功能即可,扮演防火墙的功能;
specification永远不要告诉方法的局部变量和私有类型;

内容包括:
输入、输出的数据类型;功能和正确性;性能等等。

下面是Java class BigInteger中add()操作的文档

public BigInteger add(BigInteger val)
Returns a BigInteger whose value is (this + val)
**Parameters:**
val - value to be added to this BigInteger
**Returns:**
this + val

Behavioral Equivalence(行为等价性)
站在客户端视角看, 如果能够实现相同的功能, 符合同一个规约, 则就是行为等价, 可以相互替换;

3.2.2 Specification Structure

pre-condition & post-condition(前置条件和后置条件,亦为requires & effects)

StructurePre-condition(前置条件)Post-condition(后置条件)
Keywordrequireseffects
ObligationOn the client(对客户端的约束, 在使用方法时必须满足的条件)On the implementer(对开发者的约束, 方法结束时必须满足的条件)
CommentPut into @param where possiblePut into @return and @throws
contract(契约)If the precondition holds, and then the postcondition must hold(前置条件满足, 则后置条件必须满足; 前置条件不满足, 则方法可做任何事情)

Examples:

/**
* Find a value in an array.
* @param arr array to search, requires that val occurs exactly once in arr
* @param val value to search for
* @return index i such that arr[i] = val
*/
static int find(int[] arr, int val)
    requires: val occurs exactly once in arr
    effects: returns index i such that arr[i] = val

关于specification和mutability
除非在后置条件里声明过,否则方法内部不应该改变输入参数;
尽量遵循此规则,尽量不设计mutating的方法;
关键在于“不可变”,在规约里限制住(比如,返回一个字符数组,要求不能改变,如果只在注释,@return中限制,则不可靠;最好的方法是返回类型设置为String,是不可变的,这样在规约里面限制,更可靠)。

3.2.3 Design Specifications

Classifying specifications

Deterministic(确定性)Declarative(陈述性)Strong(强度)
Deterministic(确定的规约)Under-deterministic(欠定的规约)Non-deterministic(非确定的规约)Operational(操作式规约)Declarative(声明式规约)Stronger(强规约)Weaker(弱规约)
给定一个满足pre-condition的输入, 输出是唯一的、明确的(eg. occurs exactly once in array)同一输入可以有多个输出, 通常有确定的实现,欠定的规约通常有确定的实现(eg.不同的遍历方式,遍历顺序不同, 产生不同的结果)同一输入, 多次执行输出可能不同(eg. depending on random or timing)Give a series of steps that the method performs(eg. pseudocode, 伪代码)Give properties of the final outcome, and how it’s related to the initial state(没有内部实现的描述, 只有”初-终”状态, 更具有价值)S2和S1,如果S2前置条件更弱更少, 条件更少,而后置条件更强, 要求更多更细,则S2>=S1,可以用S2代替S1(spec变强:更放松的前置条件+更严格的后置条件)

Example Of Stronger & weaker specs
举一个强和弱规约的例子
这里写图片描述
在这里,第三个规约的前置条件更弱(包含第一个的情况),而后置条件更强(被第一个情况包含),则可以用第三个规约实现的代码代替第一个规约实现的代码。因为第一个为第三个的一种情况,所以第一个仍然可以得到正确的结果。

Diagramming Specifications
Note1

A specification defines a region in the space of all possible
implementations. (某个具体实现, 若满足规约, 则落在其范围内; 否则, 在其之外)

Note2

When S2 is stronger than S1, it defines a smaller region in this diagram. A weaker specification defines a larger region.
S1要求找一个或多个元素,且返回的下标任意,可以第一个也可以最后一个(从头遍历或从尾遍历);S2要求找一个或多个元素,返回下标要限制,必须是第一个(从头遍历);
S2与S1前置条件相同,S2后置条件更强;S2更强,定义了一个小区域;S1较弱,定义了一个大区域;(这里可以体会到,S2可以代替S1,S2是S1的一种情况)
Diagram

Quality Of Specifications

Coherent(内聚的)Inormative(信息丰富的)Strong enough(足够强)Weak enough(足够弱)Abstract types(抽象类型)Precondition & Postcondition ?
Spec描述的功能应单一、简单、易理解信息足够的丰富, 不能让客户端产生理解的歧义Not only give clients a strong enghou guarantee in the general case(一般情况), but also use extra care when specifying the special cases(特殊情况)(eg.说明输入null的解决方法)太强的spec, 在很多特殊情况下难以达到在规约里使用抽象类型, 可以给方法的实现体与客户端更大的自由度(eg.泛型)不限定太强的precondition,而是在postcondition中抛出异常:输入不合法(是否使用前置条件取决于(1)check的代价;(2)方法的使用范围)
3.3 Abstract Data Type(ADT)

本节将数据和操作复合起来,构成ADT,学习ADT的核心特征,以及如何设计好的ADT。
本节考点:
ADT操作的四种类型
表示独立性
表示泄露
不变量、表示不变量RI
表示空间、抽象空间、AF
以注释的形式撰写AF、RI

3.3.0 About ADT

抽象数据类型(Abstract Data Type ,ADT)
ADT是一个实现包括储存数据元素的存储结构以及实现基本操作的算法。ADT是由操作定义的,与其内部如何实现无关!强调“作用于数据上的操作”,程序员和client无需关心数据如何具体存储的,只需设计/使用操作即可。

举个例子,对于抽象数据类型Bool的定义
包含如下操作,后面是操作的返回值,不需要管操作的具体实现
true: Bool
false: Bool
and: Bool X Bool -> Bool
or: Bool X Bool -> Bool
not Bool -> Bool

3.3.1 Abstract Data Types

ADT操作的四种类型

Data TypesOperations Types
Mutable types(可变类型的对象)Immutable types(不可变类型的对象)Creators(构造器)Producers(生产器)Observers(观察器)Mutators(变值器)
提供了可改变其内部数据的值的操作其操作不改变内部值, 而是构造新的对象创造相同类型的新对象;可能实现为**构造函数**或**静态函数(工厂方法)** (eg. Integer.valueOf(),确保不变量为true)根据一定规则生成新的对象(eg. **BigInteger.mod()**;String.toUpperCase();**Collections.unmodifiableList()**;String.concat(),String X String -> String, 确保不变量为true)传入该类型对象,返回不同类型的对象;得到对象内部的属性情况 (eg. Set.contains(), List.size() 保持不变性)改变该对象属性的方法,返回类型通常为void,也可能为非空类型(eg.List.add()(返回boolean);Coponent.add()(返回对象本身);Collections.copy();**BufferedReader.readLine()**(向缓冲区添加字符串), 保持不变性),**不可变类型对象没有变值器**

以int,String和List类型举例
int 是不可变类型,没有变值器
creators(构造器): 0,1,2, ….
producers(生产器): +,-,*,/
observers(观察器): ==,!=,<,>
mutators(变值器): none
……………………..
String是不可变类型
creators(构造器): String构造方法
producers(生产器): concat,substring,toUpperCase,(连接操作,子串,转为大写)
observers(观察器): length,charAt
mutators(变值器): none
……………………..
List是可变类型,并且是一个接口,ArrayList和LinkedList等类实现了该接口
creators(构造器): ArrayList和LinkedList构造函数,Collections.singleonList
producers(生产器): Collection.unmodifiableList,(产生一个不可更改的list)
observers(观察器): size,get,(获得list的大小或某个位置的元素)
mutators(变值器): add,remove,addAll,Collections.sort,(改变了内部元素个数或顺序)

设计ADT的原则
设计好的ADT,靠“经验法则”,提供一组操作,设计其行为规约 spec
①设计简洁、一致的操作
②要足以支持client对数据所做的所有操作需要,且用操作满足client需要的难度要低
③要足以支持client对数据所做的所有操作需要,且用操作满足client需要的难度要低

测试ADT
测试creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec;
测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确;
测试时,需要划分输入空间,此为后话。

3.3.2 表示独立性 & 表示泄露

表示独立性
表示独立性(Representation Independence) - private fields: client使用ADT时无需考虑其内部如何实现, ADT内部表示的变化不应影响外部spec和客户端
(可以改变实现方式以提高性能,但返回给客户的结果不能变;例如,List提供的操作是独立的,不用管是作为linked list还是array来实现)
spec规定client和implementer之间的契约:除非ADT的操作指明了具体的precondition和postcondition, 否则不能改变ADT的内部表示

表示泄露
首先提出不变量的概念(不变量:在任何时候总是true,由ADT负责不变量,与client的任何行为无关);
然后说明需要不变量的原因(保持程序的正确性,容易发现错误),当Immutability作为不变量时没问题;
但是,当mutable时, …,会发生表示泄露(影响不变性,以及表示独立性,改变时影响客户端);
解决方式,
成员变量,变成不可变的(private仅类内部可见,final保证不变性);
但声明为final也不一定有用,调用方法,改变了传入参数的值(如下图,传入对象t,获取t的timestamp属性,并在该属性基础上,改变原值,构建新的Tweet对象)

public static Tweet retweetLater(Tweet t){
    Date d = t.getTimestamp();
    d.setHours(d.getHours()+1);
    return new Tweet("rbmllr", t.getText(), d)
}

改变结果如下,传入参数t的timestamp值被改变了

终极解决方法—Defensive copying(Copy和clone(),clone()默认的Object中的操作浅拷贝,首先和new操作类型,创建新的地址,然后将原对象的各个属性填充到clone生成的对象中,所以改变生成对象的属性,同样会改变原对象的属性;如果要实现深拷贝,则对象需要实现Cloneable接口,并重写clone方法;对于mutable对象可以使用clone()进行赋值,但并不支持所有对象);
最好的办法还是使用immutable类型,彻底避免表示泄露;
总结:表示不变性避免表示泄露,是ADT最重要的一个Invariant

3.3.3 Comment :RI & AF & Safety from Rep Exposure

表示空间、抽象空间、AF
表示空间(Representation,R): ADT实现者关注R,
抽象空间(Abstract,A): 抽象值构成的空间,用户看到和使用的值,用户关注A;
AF(Abstraction Function,抽象函数): R和A之间映射(mapping)关系的函数,一定是满射未必单射未必双射
Rep Invariant(表示不变量):从表示值映射到booleans,R -> boolean;如果AF映射r,表示为RI(r)
描述R,A,RI的关系:

Document RI & AF & Safety from Rep Exposure
在代码中用注释形式记录AF和RI

Rep Invariant(RI, 表示不变量)Abstraction Function(AF)Safety from Rep Exposure
所有表示值的一个子集,包含所有合法的表示值, 随时检查RI是否满足(定义一个函数**checkRep()**,通过一些属性检查变量是否满足要求, 使用assert用法, 在每个函数返回之前调用checkRep();默认所有对象不为**null**)R A之间映射关系的函数(描述类的实现)给出理由, 证明代码并未对外泄露其内部表示-自证清白
通过creators和producers创建对象时要确保不变量为true,执行mutators和observers要保持不变性,并且没有表示泄露;eg. 在一个三角形的类中, 三条边要始终满足关系, 在每次赋值或者改变时都要都用checkRep()检查做出具体的解释: 每个rep value是如何映射到abstract value中的eg1. All fields are private.(私有类型) eg2. Rep1 is String, so are guaranteed immutable.(不可变类型保证不变) eg3. Obj1 is a mutable Object, so Func1 and Func2 make defensive copies(可变对象, 在使用时, 要防御式拷贝)

举个表示不变量的例子

 class C{
             private String s;
             private String t;
 }

其中为C的表示不变量的描述为(有s的组成,s和t的限制-长度,第三个不明确)

esp,可以用不变量取代方法的Precondition。

Note: ADT的规约里只能使用client可见的内容来撰写,包括参数返回值异常等。
如果规约里需要提及“值”,只能使用A空间中的“值”
ADT的内部表示(私有属性)对外部都应严格不可见。
故在代码中以注释的形式写出AFRI而不能在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏

3.3.4 Design Good ADT

设计ADT:
(1) 选择R和A;
(2) RI — 合法的表示值;
(3) 如何解释合法的表示值 —映射AF

3.4 Object-Oriented Programming(OOP)

本节学习ADT的具体实现技术:OOP,用OOP/接口/类实现ADT
考点:
接口、抽象类、具体类
继承、override
多态、overload
泛型

3.4.1 Criteria of Object-Orientation
Criteria of Object-Orientation(面向对象编程的标准)
Genericity(泛型)Interitance(继承)Polymorphism(多态)Dynamic dispatch/binding(动态分派/绑定)
3.4.2 Basic concepts
ObjectClassInterfaceComparing
An object is a bundle of state(data fields) and behavior(actions methods).A class defines methods and fields. Class defines both type and implementation. Loosely speaking, the methods of a class are its Application Programming Interface(API). Include class variable(类变量, 区别instance variables-实例成员变量) and class methods(类方法, 区别instance methods-实例方法)An interface in Java is a list of method signatures, but no method bodies.(接口只有方法的签名, 即抽象的方法,没有构造方法,成员变量,实现体) A class implements an interface if it declares the interface in its implements clause, and provides method bodies for all of the interface’s method.(一个类实现一个接口就要实现接口中的所有方法)Interface(所有方法都是抽象的) -> abstract class(有至少一个抽象方法的类,使用**abstract**标识) -> class
Interface和Class:定义和实现ADT;接口: 确定ADT的规约, **一个接口可以继承(extends)多个接口**, **一个接口可以有多种实现** ;类: 实现ADT, **一个类只能继承一个类(**extends,单继承), **一个类可以实现多个接口**(implements,多继承)

关于Static
静态变量,静态方法属于整个类所有,所以可以直接用类名访问(eg. B.method2());静态成员变量自第一次使用,分配内存空间后,次后一直使用该空间;静态方法可以直接调用类中静态变量,但不能直接调用非静态变量/方法;
静态方法调用使用同一个栈,而一般方法调用,每次调用时使用不同的栈
这里写图片描述

3.4.3 Distinc features of OOP

verriding- overriding while final and static
diff of overloading and overriding

Encapsulation(封装)Interitance(继承)**Polymorphism**(多态)
Information hiding(信息隐藏)**Overriding**(重写)Ad hoc polymorphism(特殊多态,**Overloading**, 重载)Parametic polymorphism(参数化多态,**Generics,** 泛型)Subtyping(子类型多态、包含多态)
隐藏所有的实现细节,客户端仅**使用接口类型声明变量**(InterfaceA var = new SubClass()),客户端代码无法直接访问属性; Benefits of information hiding(Decouples, 解耦; Speeds up system development,加速; Eases burden of maintenance,维护; Increases software reuse, 复用; Enables effective performance tuning,性能完全相同的signature-类型和参数, **实际执行时调用哪个方法在运行时决定**(在**run-time**进行**动态检查**), 重写不要改变方法的本意多个方法具有同样的名字, 但有**不同的参数列表**或**返回值类型**, 方便client调用, client可用不同的参数列表, 调用同样的函数。Overloading is a static polymorphism.(**静态类型检查**, 在**编译阶段**决定具体执行哪个方法)**types to-be-specified-later**,包括泛型接口(public interface List,可以用任意类型替代E;可以有非泛型的实现类,**public class ArrayList implements List**;或泛型的实现类,**public class ArrayList implemetns List**),泛型方法(**public T get(int index)**)A subtype is simply a subset of the supertype.**子类型的规约不能弱化超类型的规约**, 不同类型的对象可以统一的处理而无需区分。
Visibility modifiers for members: **private**-类中可见;**protected**-子类(不一定在同一个包中)或同一个包中可见;**无修饰词**-同一个包内可见;**public**-任意可见**final标识的变量不能改变**, **final标识的类不能继承**, **final, static, private标识的方法无法在子类中重写**(严格继承);由于是**静态类型检查**,只看最开始定义的类型, 在Atype var = new ASubClass()中, **调用的是父类**Atype中的方法;super.thisfunction(param),调用父类的该方法**不同的参数列表**(不能只是参数名不同,必须是参数类型或参数个数不同), **相同/不同的返回值类型**, **相同或不同的public/private/protected**,不同的异常, 可以在同一个类内重载, 也可以在**子类中重载**泛型变量, 泛型类, 泛型接口, 泛型方法; **Wildcards通配符(?)**(eg. List
3.4.4 Object methods
equals()hashCode()toString()
– 身份语义, 两个对象相等,返回true(重写时,需要使用instanceof判断类型是否相同)– 身份语义, a hash code for use in hash maps/list/set(Override, if int number, add dirctly, if String, **str.hashCode()**,eg. a = 23, b = “message”, hashcode = a + b.hashCode(),或a*17/31 + b)– ugly and uninformative, 用于打印展示
3.5 Equality in ADT and OOP

本节考点:
等价性equals()和==
equals()的自反、传递、对称
hashCode()
可变对象的观察等价性、行为等价性

3.5.1 About equality
Equality operation on an ADT Equality of values in a data type
1. ADT是对数据的抽象, 体现为一组对数据的操作, 而不是数据的表示 2. 抽象函数AF: 内部表示 -> 抽象表示 3. 基于抽象函数AF定义ADT的等价操作 Two physical objects are nerver truly equal to each other; they only have degrees of similarity.
3.5.2 Three ways to regard equality
Using AF Using a relation Using observation
a equals b if and only if f(a) = f(b), AF映射到同样的结果, 则等价. An equivalence is a relation(等价关系) that reflexive(**自反**), symmetric(**对称**), transitive(**传递**) Two objects are equal when every operation we can apply produces the same result for both objects.(站在外部观察者角度, client/ outsider)
3.5.3 == Vs equals()
**==** **obj.equals()**
compares reference equality(**引用等价性**, 对**基本数据类型**(for primitives types), 使用==判定相等)compares object equality(**对象等价性**, 对**对象类型**(for object reference types), 使用equals())
== 是在判断两个对象身份标识ID是否相等(指向**内存里的同一段空间**)在自定义ADT时, 需要合理地**重写**Object的**equals()**方法
Tips for overriding a method: 1. Make sure **signatures match**(签名相同) 2. Use **@Override** so compiler has your back(使用标识) 3. Do **copy-and-paste** declaration(声明一致)

Note
在Object中实现的缺省的equals()是在判断引用等价性this == that
这通常不是程序员所期望的,因此,需要重写
@Overrde
public boolean equals(Object obj)
需要使用instanceof判断是否为null
参数必须是Object类型,如果改为对象类型,则为overload

3.5.4 Equality of mutabel and immutable types
Equality of **immutable** typesEquality of **mutable** types
override equals() with our own implementation,重写equals()和hashCode()方法(**List、Set等集合类的contains()方法是由equals()和hashCode()的实现的**) Observational equality(**观察等价性**): 在不改变状态的情况下,两个mutable对象是否看起来一致;Behavioral equality(**行为等价性**): 调用对象的任何方法都展示出一致的结果;对可变类型来说,往往倾向于实现严格的观察等价性
The equals contract(重写equals()的契约), 1. Reflexive(**自反**, every object equals to itself) 2. Symmetric(**对称**, if a.equals(b) then b.equals(a)) 3. Transitive(**传递**, if a.equals(b) and b.equals(c), then a.equals(c)) 4. Consistent(equal objects stay equal unless mutated) 5. **Non-null**(a.equals(null) return false);
The hashCode contract(equals(Object) and hashCode return the same integer result,两个equal的objects,一定要有同样的hashcode,根据equal比较的内容来重写hashcode): ①Equal objects must have equal hash codes(override hashCode when override equals) ②不相等的对象可以映射为同样的hashCode,但性能会变差 ③除非一个object改变,否则hashCode不能改变
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值