总结:本小节讲述了关于设计数据类型的重要知识,包括封装、设计API、算法与抽象数据类型的关系、接口继承、实现继承、等价性、内存管理、不可变性、异常与错误等知识。(“简单看来,下面的许多话题和算法的学习关系不大,因此你可以跳过本节,在今后实现抽象数据类型中遇到特定问题时再回过头来参考它。”)
重点:
1. 设计一份优秀的API所付出的所有时间都能从调试和代码复用所节省的时间中获得回报。
2. 在本书中,算法一般都是某个抽象数据类型的一个实例方法的实现。
3. 在本书中我们会避免使用子类继承,因为他会破坏封装。
4. Java约定equals()必须是一种等价性关系。它必须具有:
· 自反性,x.equals(x)为true;
· 对称性,当且仅当y.equals(x)为true时,x.equals(y)返回true;
· 传递性,如果x.equals(y)和y.equals(z)均为true,x.equals(z)也将为true。
另外,它必须接受一个Object为参数并满足以下性质:
· 一致性,当两个对象均未被修改时,反复调用x.equals(y)总是会返回相同的值;
· 非空性,x.equals(null)总是返回false。
5. 可以使用下面的实现作为实现任意数据类型的equals()方法的模版:
public class Date { private final int month; private final int day; private final int year; public Date(int m, int d, int y) { month = m; day = d; year = y; } public boolean equals(Object x) { if (this == x) return true; if (x == null) return false; if (this.getClass() != x.getClass()) return false; Date that = (Date) x; if (this.day != that.day) return false; if (this.month != that.month) return false; if (this.year != that.year) return false; return true; } }
6. Java最重要的一个特性就是自动内存管理。它通过记录孤儿对象并将它们的内存释放到内存池中,将程序员从管理内存的责任中解放出来。这种回收内存的方式叫做垃圾回收。
7. 不可变性的另一个缺点在于,final非常不幸地只能用来保证原始数据类型的实例变量的不可变性,而无法用于引用类型的变量。如果一个应用类型的实例变量含有修饰符final,该实例变量的值(某个对象的引用)就永远无法改变了--它将永远指向同一个对象,但对象的值本身仍然是可变的。例如,这段代码并没有实现一个不可变的数据类型:
public class Vector { private final double[] coords; public Vector(double[] a) { coords = a; } ... }
用例程序可以通过给定的数组创建一个Vector对象,并在构建函数执行之后(绕过API)改变Vector中的元素的值:
double[] a = {3.0, 4.0}; Vector vector = new Vector(a); a[0] = 0.0; // 绕过了公有API
8. 任何数据类型的设计都需要考虑到不可变性,而且数据类型是否是不可变的则应该在API中说明,这样使用者才能知道该对象中的值是无法改变的。
9. 默认设置没有启动断言,可以在命令行下使用 -enableassertions 标志(简写为-ea)启动断言。断言的作用是调试:程序在正常操作中不应该依赖断言,因为他们可能会被禁用。系统编程课程会学习使用断言来保证代码永远不会被系统错误终止或是进入死循环。(?)
10. 如果你总是抱怨编程语言的特性,那么你只能自己设计编程语言了。(霸气!)