软构随笔复习2

ADT的4种操作:
• ①构造器creator,输入一些其它类型的对象或者没有输入,创建一个该ADT对象。
或者现实中的构造函数。
• ②生产器producer,通过该ADT的旧对象,创建一个该ADT的新对象。
• ③观察器observer,通过该ADT本身的数据以及传入参数,计算得到其它类型的值。
• ④变值器mutator,作出“修改ADT内部数据”的行为,是可变对象与不可变对象的本质区别。变值器不一定返回void。

接口相当于规定了ADT所需的未实现的操作(方法),这是用户所关注的。类真正地在代码层面实现了接口规定的ADT操作,并且实现了ADT内部的数据存储。
类可同时实现多个接口。
Java中接口中的方法默认为public。
类中实现接口的方法可以不用加Override,编译器会自动识别。
类中可以有自己独有的方法,无法通过接口调用。

接口实际上可以通过default或者static直接编写方法实现。
接口中的方法原则上是不应该private的,但Java9开始可以,不过必须在接口中给出具体实现。
一个接口可以通过extends继承另一个甚至 多个接口的方法,但显然不能implements。
final用在接口里毫无意义。

规约是客户端与实现者之间签订的“契约”,客户端的输入应当满足前置条件,实现者编写的程序应当给出满足后置条件的结果。
规约描述了方法的功能以及接口(“能做什么”),不需要依赖(也不应该透露)方法的具体实现。ADT中的方法规约在接口就应该声明,不应该跟具体实现有关。
如果客户违反了前置条件,那么程序也不需要满足后置条件,做什么都可以。

前置条件(参数要求),后置条件(返回值要求,或者throws抛出的异常)
更强的规约:前置条件更弱,后置条件更强。规约图:小的区域表示强的规约。

客户端可以看到ADT的操作方法及规约,可以看到抽象空间以及抽象值,但就是不能看到rep、RI以及AF,它们涉及到客户端不应该知道的与内部实现有关的细节。
一个便于理解但不太准确的例子,对于黑盒函数 𝒇 (𝒙) = 𝐥𝐧𝒙 ,rep就是x,AF就是表达式𝐥𝐧𝒙 ,表示空间和抽象空间都是实数域,RI为x>0的限制
对于ADT,客户端是无法看到其内部表示属性(rep)的,客户端只会看到ADT在表面上展现出来的东西,即,ADT做了一个由表示空间®到抽象空间(A)的映射AF。
不是所有表示属性rep都能映射到相应的抽象值的(即有一些rep是非法的),那么任何时刻ADT的rep都必须满足一定规则(合法),即表示不变量RI。有时应该引入一个方法checkRep检查rep是否满足RI。

对于不可变类型,它的抽象值一定是不能变的,但表示值可以改变,前提是改变后通过AF仍然映射到相同的抽象值。

表示独立性是指,客户端使用ADT时无需考虑(也不应该知道,更不应该直接访问到)其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端。
如果ADT不幸地让客户端得到了自己内部表示(可变对象)的引用,那么客户端就可以不通过ADT的操作,而可以通过非法后门修改ADT的内部表示,产生表示泄露。

对于一个表面类型为父类的引用变量a,通过a.b()调用方法时,在运行时将会动态地检查a是否实际指向一个子类型对象,子类型是否重写了b()方法,如果是那么则调用子类型的重写方法。这一机制实现了子类型多态,即同样的a.b(),可能调用不同的方法。
一个类child可以继承另一个类father的全部方法与属性,在运行时存在继承关系的类型对象可以互相转换。子类型也可以重写(override)父类的方法,替换为自己的实现代码。
extends指定父类,Java只允许一个父类。
被声明为final的方法子类不可重写。

容器类可以有不同的底层实现,但它们的操作都较为统一,表示为接口实现的形式。
例如,ArrayList和LinkedList都实现了接口List,都能实现列表的统一操作,但分别基于数组和链表实现。HashMap<K,V>和TreeMap<K,V>都实现了接口Map<K,V>,都能实现映射的统一操作,但分别基于哈希表和红黑树实现。HashSet和TreeSet都实现了接口Set,都能实现集合的统一操作,但分别基于哈希表和红黑树实现。
• 所有引用类型的最终祖先类都是Object,都具有等价性判断equals()、哈希函数hashCode()和字符串转换toString()方法,可以重写它们。
• Integer、Double等表示数值的引用类型(跟int、double这些非引用的内置类型不一样)都继承自Number类,都具有转换为其它数值类型的方法(例如intValue())。
• 所有异常类都继承自Exception,对于父异常的catch能够捕获子异常。

ADT的等价性是对于客户端角度而言的,要么,两个对象通过AF映射到相同的抽象值,要么,两个对象能做出效果相同的行为。不一定非得让rep完全一致。
• 所有对象都继承了Object.equals(),我们可以在类中重写它,从而定义自己的等价规则。注意equals()与==不一样,后者仅仅判断两个引用是否指向同一对象。如果不重写Object.equals(),那么默认效果和==是一样的。
• 对于可变对象,除了上述的“观察等价性”,还会有一种“行为等价性”,如果两个对象这个时刻等价,那么不管之后干了什么,这两个可变对象仍然是等价的。(一般和==一样,当且仅当是同一对象,例如StringBuilder,我们常用的除它之外的类一般都是观察等价性,如String、List)

Object.hashCode()计算对象的哈希值,将对象映射为一个整数,使得等价的对象具有相同的哈希值。好的哈希算法应当使得不等价对象的哈希值尽量不同。
• hashCode()一般用在基于哈希表的集合类中(HashMap、HashSet)。查找一个对象时,先通过hashCode直接找到对象对应的桶,再在桶里一个个用equals比较。
• Object.hashCode()默认直接返回对象的地址,即对象哈希值相同当且仅当为同一个对象(引用一样),当使用了自定义的等价规则时,需要重写hashCode。

重载(overload)机制使得同一个类中的多个方法可以有相同的名字,前提是它们有长度不同的参数列表,或者对应不同的参数类型,起码,得让编译器在进行静态检查的时候通过你调用时传入的参数判断实际上应该选择哪个方法。重载也可以发生在父类与子类之间(子类重载父类的方法)。但如果两个重载方法的参数列表十分接近,编译器决策不了到底应该调用哪个,静态检查错误。
重载函数的返回值 可以没有任何关系。

泛型是参数化多态的实现机制,它能够将类型作为类/方法的参数,使得操作与类型无关。泛型不存在于运行时!

3种基本的多态:
①特设多态(同一操作,不同类型不同行为),重载
②参数化多态(操作与类型无关),泛型
③子类型多态(同一对象可能属于多种类型),继承/重写

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值