基本类型的包装类型:通常是在定义集合类型的时候使用它们;一般情况下,尽量避免使用;一般可以自动转换
静态类型检查 >> 动态 >> 无检查
静态类型检查:在编译阶段进行类型检查;静态类型检查:可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性;包括:语法错误、类名/函数名错误、参数数目/类型错误、返回值类型错误。
动态类型检查:在运行阶段进行类型检查;包括:非法的参数值、非法的返回值、越界、空指针。
Mutability:改变一个变量:将该变量指向另一个值的存储空间;使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据;如StringBuilder。
Immutability:改变一个变量的值:该变量当前指向的值的存储空间中写入一个新的值;用final可以使得变量不可变;如果编译器无法确定final变量不会改变,就提示错误,这也是静态类型检查的一部分;不可变类型更“安全”,在其他质量指标上表现更好。
可以使用Collections.unmodifiableList、List.of()类型转化为不可变类型,但是这种“不可”是在运行阶段获得的,编译阶段无法据此进行静态检查。
Snapshot:
基本类型:
对象类型:
不可变对象:引用不变(双线箭头),值不变(两层圆圈)。
List类型要标注,0,1,2…;Set类型不用;Map类型要画成对出现紧贴的方块。
Spec:
作用:规约可以隔离“变化”,无需通知客户端;规约也可以提高代码效率;扮演“防火墙”角色;解耦,不需了解具体实现。
强弱判断:
这样的规约是更强的:前置条件更少,后置条件更多(要的更少,给的更多)
比较方法:
如果前置条件变弱,后置条件不变,则视为规约变强
如果前置条件不变,后置条件变强,则也视为规约变强
如果前置条件变弱,后置条件也变弱,则无法比较
如果规约 S2的强度强于S1,那么就可以用S2代替S1。
越强的规约,意味着implementor的自由度和责任越重,而client的责任越轻
越强大的规约圈越小,因为度降低了。
太弱的spec,client不放心、不敢用 (因为没有给出足够的承诺),发者应尽可能考虑各种特殊情况,在post-condition给出处理措施。太强的spec,在很多特殊情况下难以达到,给开发者增加了实现的难度(client当然非常高兴)。
行为等价性:站在客户端视角看行为等价性,根据是否符合同一个规约来判断是否等价。
观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致。
对可变类型来说,往往倾向于实现严格的观察等价性,对不可变类型来说,不存在行为等价性,只存在观察等价性,因为不可变类型不能发生变化。
ADT中方法的四种类型:
creator:(从无到有)创建该类型的新对象,如new或静态工厂方法、String.valueOf(Object Obj)等。
producer:(从有到新)从该类型的旧对象创建新对象,如String的concat()方法
observer:获取抽象类型的对象并返回不同类型的对象,如.size()
mutator:只有可变数据类型才有,是改变对象属性的方法,如list .add(),StingBuilder .append()。
表示独立性:client使用ADT时无需考虑其内部如何实现,ADT内部表示的变化不应影响外部spec和客户端;除非ADT的操作指明了具体的pre和post-condition,否则不能改变ADT的内部表示——spec规定client和implementer之间的契约。
测试ADT:测试creators, producers, and mutators:调用observers来观察这些operations的结果是否满足spec;测试observers:调用creators, producers, and mutators等方法产生或改变对象,来看结果是否正确。
不变量:
immutability就是一个典型的“不变量”; 保持不变性和避免表示泄漏,是ADT最重要的一个Invariant;任何时候总是true;由ADT来负责其不变量,与client端的任何行为无关。
避免表示泄露的方法:
使用final、private,防御性编程,再利用可变类型初始化时可以建立一个新对象再复制外部传入的对象。
AF:抽象值构成的空间:client看到和使用的值;ADT开发者关注表示空间R,client关注抽象空间A;满射,未必单射,未必双射;抽象函数(AF)R和A之间映射关系的函数,即如何去解释R中的每一个值为A中的每一个值;即使是同样的R、同样的RI,也可能有不同的AF,即“解释不同”。
有些mutation只是改变了R值,并未改变A值,对client来说是immutable的 →“AF并非单射”,从一个R值变成了另一个R值,但这并不代表在immutable的类中就可以随意出现mutator!
故在代码中以注释的形式写出AF和RI而不能在Javadoc文档中,防止被外部看到而破坏表示独立性/信息隐藏,不能向外部泄露任何内部表示的细节,以及R空间中的任何值。
接口:接口之间可以继承与扩展;一个类可以实现多个接口(从而具备了多个接口中的方法);一个接口可以有多种实现类。接口中的每个方法在所有类中都要实现;通过default方法在接口中统一实现某些功能,无需在各个类中重复实现它。
抽象类:至少有一个抽象方法;不能实例化;子类中每一个抽象方法都要重写。
多态:特殊多态(功能重载)、参数化多态(泛型)、子类型多态。
重写与重载
泛型使用示例:
注意:在运行时泛型就消失了,所以不能用instanceof等函数。
等价性:
AF映射到同样的结果,则等价。
引用等价性和对象等价性:如果用==,是在判断两个对象身份标识 ID是否相等(指向内存里的同一段空间);要判断对象等价性就要重写@Override public boolean equals(Object o);
等价的对象必须有相同的hashCode,而不等价的对象可以有相同的hashCode。
观察等价性和行为等价性:观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致;行为等价性:调用对象的任何方法都展示出一致的结果。对可变类型来说,往往倾向于实现严格的观察等价性,但也有例外,例如如果某个mutable的对象包含在HashSet集合类中,当其发生改变后,集合类的行为不确定务必小心。Date:观察等价性;List:观察等价性;String Builder:行为等价性(继承自Object类)。
对于不可变类型,一定要重写equal和hashcode方法,对于可变类型则没有必要。