复习(2)

静态/动态类型检查:

静态类型检查:可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正 确性/健壮性,检查语法错误、类名/函数名错误、参数数目错误、参数类型错误、 返回值类型错误

动态类型检查:在运行阶段发现错误,检查非法的参数值,非法的返回值,越界,空指 针。

静态检查:关于“类型”的检查,不考虑值

动态检查:关于“值”的检查

Mutable/Immutable

改变一个变量:将该变量指向另一个值的存储空间。

改变一个变量的值:将该变量当前指向的值的存储空间中写入一个新的值。

Immutability 不变性:

不变数据类型:一旦被创建,其值不能改变

如果是引用类型,也可以是不变的:一旦确定其指向的对象,不能再被改变

如果编译器无法确定final变量不会改变,就提示错误,这也是静态类型检查的一部分。 所以,尽量使用final变量作为方法的输入参数、作为局部变量。

关于final:final 类无法派生子类

final 方法无法被子类重写

final 变量无法改变其引用(而非值)

不变对象:一旦被创建,始终指向同一个值/引用

可变对象:拥有方法可以修改自己的值/引用

对于不可变类型,频繁对其运算会产生大量的临时拷贝,可变类型能最少化拷贝以提高效率。因此,使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据。

而不可变类型更“安全”, 在其他质量指标上表现更好。例如,向任何方法传入不可变类型的值,不用担心其值被意外修改;多线程可以安全地持有同一个不可变类型的引用,而不用担心竞争。

保护可变数据类型:

防御式拷贝:给客户端返回一个全新的对象。大部分时候该拷贝不会被客户端修改, 可能造成大量的内存浪费

单引用局部变量:把对 mutable 对象的引用限制在类/方法内,不对外暴露。如果存在多个引用,使用可变类型就非常不安

Snapshot diagram

Snapshot diagram用于描述程序运行时的内部状态,便于程序员之间的交流,便于刻画各类变量随时间变化,便于解释设计思路

可变类型对象的椭圆使用单线,而不可变类型的椭圆使用双线。

可变引用箭头使用单线,不可变引用箭头使用双线。

引用是不可变的,但指向的值却可以是可变的;可变的引用,也可指向不可变的值。

Specification、前置/后置条件

前置条件:对客户端的约束,在使用方法时必须满足的条件

后置条件:对开发者的约束,方法结束时必须满足的条件

契约:如果前置条件满足了,后置条件必须满足

除非在后置条件里声明过,否则方法内部不应该改变输入参数

尽量避免使用mutable的对象

前置条件更弱,后置条件更强,就可以用S2替代S1

欠定的规约:同一个输入可以有多个输出

非确定的规约:同一个输入,多次执行时得到的输出可能不同

是否使用前置条件取决于(1) check的代价;(2) 方法的使用范围

如果只在类的内部使用该方法(private),那么可以不使用前置条件,在使用该方法的各个位置进行check——责任交给内部client; – 如果在其他地方使用该方法(public),那么必须要使用前置条件,若client端不满足则方法抛出异常。

ADT-抽象数据类型

构造器creator,输入一些其它类型的对象,创建一个该ADT对象。

生产器producer,通过该ADT的旧对象,创建一个该ADT的新对象

观察器observer,通过该ADT本身的数据以及传入参数,计算得到其它类型的值。

变值器mutator,作出“修改ADT内部数据”的行为,是可变对象与不可变对象的本质区别!如果返回值为void,则必然意味着它改变了对象的某些内部状态。

表示独立性、表示泄露:

表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。

如果ADT不幸地让客户端得到了自己内部表示(可变对象)的引用,那么客户端就可以不通过ADT的操作,而可以通过非法后门修改ADT的内部表示,产生表示泄露。需要防御式拷贝。

不变量、表示不变量RI

不变量:在任何时候总是true

不是所有表示属性rep都能映射到相应的抽象值的(即有一些rep是非法的),那么任何时刻ADT的rep都必须满足一定规则(合法),即表示不变量RI。

表示空间、抽象空间、AF:

ADT开发者关注表示空间R,client关注抽象空间A。

R到A: 满射、未必单射。

R中的部分值并非合法的,在A中无映射值。

对于不可变类型,它的抽象值一定是不能变的,但表示值可以改变(反正客户端也不知道),前提是改变后通过AF仍然映射到相同的抽象值!

以注释的形式撰写AF、RI

要精确的记录RI:rep中的所有fields何为有效。

要精确记录AF:如何解释每一个R值

接口、抽象类、具体类

接口:确定ADT规约;类:实现ADT

接口可以通过default或者static直接编写方法实现。

基于static的工厂方法。

在接口中就已实现默认操作的default方法。

继承、override(覆盖/重写):

extends 是继承某个类, 继承之后可以使用父类的方法, 也可以重写父类的方法; implements 是实现多个接口, 接口的方法一般为空的, 必须重写才能使用。

对于final的方法:严格继承:子类只能添加新方法,无法重写超类中的方法

对于final的类:不能继承

变量在代码中指定的表面类型,和运行时变量实际指向的对象类型,可能是具有继承关系的不同类型。

Integer、Double等表示数值的引用类型都继承自Number类,都具有转换为其它数值类型的方法

所有异常类都继承自Exception,对于父异常的catch能够捕获子异常。

多态、overload重载:

对于一个表面类型为父类的引用变量a,通过a.b()调用方法时,在运行时将会动态地检查a是否实际指向一个子类型对象,子类型是否重写了b()方法,如果是那么则调用子类型的重写方法。这一机制实现了子类型多态,即同样的a.b(),可能调用不同的方法。

多个方法具有同样的名字,但参数列表必须不同(个数,类型),返回值可以不同。

overload在编译阶段时static type checking

override在run-time进行dynamic checking

overload:特殊多态

override:子类型多态、包含多态

generics or generic programming:参数化多态

泛型Generics:

泛型不在运行时

数组可协变是指如果类Base是类Sub的基类,那么Base[]就是Sub[]的基类。

泛型不可变的是指List<Base>不会是List<Sub>的基类,两者压根没关系。

等价性equals()和==

ADT的等价性是对于客户端角度而言的,要么,两个对象通过AF映射到相同的抽象值,要么,两个对象能做出效果相同的行为。

==仅判断两个引用是否指向同一对象。如果不重写Object.equals(),那么默认效果和==是一样的。StringBuilder和StringBuffer因为没有重写的equal,两个new的equal返回为false。

equals()的自反、传递、对称:

重载equals方法时要遵守的通用约定--自反性,对称性,传递性

a.自反性,对于任何非null的引用值x, x.equals(x)必须返回true。

b.对称性,对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true

c.传递性,对于任何非null的引用值x,y和z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)返回true

hashCode()

Object.hashCode()计算对象的哈希值,将对象映射为一个整数。

Object.hashCode()默认直接返回对象的地址,即对象哈希值相同当且仅当为同一个对象(引用一样),当使用了自定义的等价规则时,需要重写hashCode。

“相等”的对象,其hashCode()的结果必须一致,好的哈希算法应当使得不等价对象的哈希值尽量不同。

查找一个对象时,先通过hashCode直接找到对象对应的桶,再在桶里一个个用equals比较。

不可变对象的引用等价性、对象等价性:

引用等价性:重写equal()判断等价性

对象等价性:相同的hashCode()

可变对象的观察等价性、行为等价性:

观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致。

只调用observer、producer和creator方法,不能区分两个对象。

行为等价性:调用对象的任何方法都展示出一致的结果。

调用四种方法都不能区分两个对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值