有名称,可以根据不同的目的给静态工厂起不同的名称。
可以根据需要来决定是否需要创建对象。
可以返回原类型的任何子类型的对象。 这些子类型可以是非公有的,可以根据不同的情况返回不同的类型。 允许通过注册的方式增加新的类,返回新的类的对象。
可以利用类型推导,使代码更加简单。
Map<String List<String>> m = HashMap.newInstance();
为了处理多个可选参数,有三种做法:
不足之处在于调用这需要熟悉多个函数, 然后参数之间的顺序也不够灵活, 如果参数多了,调用起来不是很方便。
先通过无参构造器来构建对象,然后调用setter来设置属性。 无法维持对象的一致性,很难做到线程安全。
先调用setter来构建builder,然后使用builder来构建对象。
builder模式可以很方便地实现抽象工厂。相比于newInstance更加类型安全。
builder模式的不足在于构建builder的开销。 所以只在参数较多的情况下才使用builder模式
这是最佳方法,相对于private构造函数, 不会有序列化的问题。 不过很少有单例能够序列化吧。
通过不可实例化的类来把一些静态方法组织在一起。
如果类没有提供公有构造器,它也不能被继承。
如果类的构造器是private的,应该通过注释说明为什么这样做。
不要依赖于终结方法来释放资源,使用try...finally的形式。
提供一个显式的终止方法,要求类的客户端必须调用此方法。 此时,终结方法可以充当‘安全网’的作用,确保客户端调用了显示的终止方法。
如果调用了终结方法,记得调用 super.finalize 。
编写好的 equals 函数:
这条没什么好说的,使用关键域来生成hashCode。
覆盖toString来使一个类的字符串表示更有意义。
覆盖clone时,要调用 super.clone 返回一个新的对象, 然后给需要拷贝的域进行拷贝,注意,这些域不能是final的。
另外一种实现对象拷贝的方式是提供一个拷贝构造器或静态工厂。 这样更加方便。
这是一个泛型接口,能够被泛型算法使用。 如果一个类实现了Comparable接口,如果要对它扩展新的域, 推荐的做法是使用组合和不是继承,这样能够满足对于Compare的对称性和传递性。
信息隐藏的目的在于可以使客户端不用关心这些东西, 一方面使得接口更加简单,容易使用。 另一方面,可以更改接口的实现, 而客户端不用做任何修改。
新的访问级别:包级私有,它是默认的访问级别。
受保护的成员属于导出API的一部分,它是在为继承而设计的类中存在的。
不要public出类的不可变域。虽然Java的setter和getter写起来有点麻烦。
如果类是包级公有的,或者是私有的嵌套类, 直接暴露它的数据域并没有本质的错误。
使类不可变,要遵循下面五条原则:
对于继承,子类依赖于其超类中特定功能的实现细节。 特别是,如果子类覆盖了超类的某个方法, 而超类的某个方法调用了这个方法, 此时就应该特别小心。
只有当子类是超类的子类型时,才适合用继承。 确定两个类之间确实有“is-a”关系, 这个原则不太好判断啊,就拿这条的例子来说, 可以算作是“is-a”关系,可以还不适合继承。 可能这个地方的原因在于原来的类不是用来扩展的, 没有用文档注明细节,如果细节被文档标明了, 这些细节就不应该被修改了。
如果一个类的API有缺陷,应该尽量使用组合而不是继承, 这样会防止把这种缺陷扩展到客户端。
类的文档必须精确地描述覆盖每个方法锁带来的影响。特别是可覆盖的方法。
好的API文档应该描述一个给定的方法做了什么工作, 而不是描述它如何做到的。对于继承类来说,这点需要修改。
对于为了继承而设计的类,唯一的测试方法就是编写子类。
构造器决不能调用可悲覆盖的方法。
无论是clone还是readObject,都不可以调用可覆盖的方法。
对于一个API的设计者,可以这么来设计,也很有道理, 导出的类只有两种,一种是专门用来继承的,一种是禁止继承的。 可是作为内部实现来说,又应该怎么做呢?
为了实现某个功能,应该会有一个类的层次, 通过这些类完成整个功能。 这时每一个类是否允许继承能够被确定吗? 而且,有些类是能被继承,但是只允许被当前包继承, 这应该怎么实现?把类声明成包级私有的? 现在主要的问题是看Java代码太少了,否则应该可以实现的。
至少对于后一种情况,文档说明的情况应该要改一下吧。
接口比抽象类灵活。为了使用抽象类的优点, 可以实现一个骨架类作为一个接口的抽象类用于继承。
现在还不是很明白骨架类的用处在哪, 它的本质还是一个抽象类。 既然接口优于抽象类, 那就直接使用接口就好了, 还需要骨架类干嘛呢?
抽象类相对于接口,演变相对容易一些(可以增加新的方法), 如果演变的容易性比灵活和功能更为重要的时候可以考虑使用抽象类。
不应该使用接口来导出常量, 直接使用一个不能初始化的类来导出就可以了。
类层次相对于标签类来说更加清晰,灵活。
通过策略接口来模拟函数指针,把一个具体的函数作为参数。 具体的策略实现类可以不公开,而只要提供一个这个类的对象在宿主类中, 通过公有的静态final域被导出。
静态成员类相对于普通成员类不需要引用外围对象,能够减少开销。 如果一个成员类不需要访问外围类, 那么就把它声明为静态成员类。
匿名类只有当它出现在非静态的环境中时,才有外围实例。
使用泛型机制能够很好地利用类型检查,能够尽快地暴露程序的错误。
如果不确定具体元素的类型,可以使用类型通配符, 而不要使用原生态类型。
注意 Set<Object> 和 Set<?> 的区别。 Set<Object> 可以包含任何对象的一个集合, Set<?> 可以包含某未知类型对象的一个集合。
Unchecked warnings 能够指明程序中的类型不匹配, 除非足够确认, 不要消除unchecked warnings。 如果消除了unchecked warnings, 需要添加注释表明为什么要这样做是安全的。
列表能够使用泛型的特性,可以依靠类型检查来做到类型安全。
在泛型类或者泛型函数的实现中,使用列表更数组更好。 但是列表操作起来有点麻烦,远不及数组方便, 这应该算是Java语言的一个不好的地方吧。
使用泛型能够更加类型安全,但是泛型里如果要使用数组也是可以的 但是需要手动进行类型转换,同时SuppressWarnings. 这就是Java泛型系统本身的局限性, 不知道以后的版本中会不会对它进行修改。
泛型方法能够自动的进行类型推导, 这与泛型类不一样, 所以对于调用着来说很方便。
这样就可以把子类型的变量放入容器了, 更加灵活。
PECS: producer-extends, consumer-super.
如果类型推导无法正确地推导出类型, 可以显式地设置参数的类型。
对于异构容器,可以在它的函数中传入类型参数, 其他的参数可以利用这个参数, 这样能够保证类型安全。
public class Favorites {
public <T> void putFavorite(Class<T> type, T instane);
public <T> T getFavorite(Class<T> type);
}
一个好处在于可以遍历,或者获取整个组的大小。
枚举的本质是int值,所以不会有性能问题。
可以给枚举类型定义域和方法,可以实现任意的接口。 与普通类的区别在于这个类只能导出特定的instance.
可以给每个instance定义同一个函数的不同实现。
可以使用枚举策略来给将一组函数组织在一起, 不用对于每个instance定义一个实现, 而且,可以防止对于新增instance而漏写case的问题。
不要依赖于 ordinal() 这个方法, 如果需要给每个实例一个序数,通过一个实例域来模拟。 对于C++,有一个默认的实现。
在C语言中,经常看到利用位操作来组织一些flag, 这样可以直接用与和或操作来提取、添加flag。 在Java中,推荐使用EnumSet来完成这样的功能。
通过EnumMap来把data和enum关联起来, EnumMap的构造器需要传入一个Class对象, 感觉有点重复了啊,因为前面泛型的类型参数指定了Enum的类型, 不知道为什么要这样设计。
感觉这点不是很重要,客户端很少需要去扩展一个枚举类吧。
利用注解,能够利用编译器的检查尽早发现错误, 而且可以传递注解参数,比命名模式方便很多。
使用 @Override 注解,能够利用编译器的检查防止不必要的错误。
待续,没怎么看明白。
对于有些参数,方法本身没有用到,却被保存起来供以后使用, 检查这类参数的有效性显得尤为重要。
如果检查有效性的代价特别昂贵, 或者需要等方法的运行才能检查方法的有效性, 这时可以不用检查参数的有效性, 但需要注意必要的时候使用异常转译。
保护性拷贝在构造函数(传入数据)时和返回对象的域(传出数据)时。 对于传入数据,保护性拷贝在检查参数的有效性之前进行的。
只要用可能,都使用不可变对象作为对象的组件(域)。这一点比较关键。
如果信任客户端不会不适当地修改组件, 就可以不用进行保护性拷贝来增加性能。
注意重载不要引起冲突, 防止混淆。重载方法的选择是在编译是决定的。 而对于被覆盖方法的选择是在运行时决定的。
慎用可变参数,尤其是类型是Object类型,或者是T类型。
How to write Doc Comments 这个规范.
每个局部变量的声明都应该包含一个初始化表达式, 必须使用try来初始化的变量例外。
for 循环优先于 while 循环。
} // 避免每次迭代都计算n
方法小而集中。
使用for-each循环,代码更加简短,也不容易出错。
不要重复造轮子。
如果需要精确的答案,使用BigDecimal。
不要使用使用装箱类型来进行 == 比较。
使用其他的类来组织数据。
如果需要把很多的字符串连接到一起, 使用StringBuilder更好, 能够减少字符串拷贝。
好处在于可以把对象改成其他实现了该接口的类型的对象, 而且保证不会依赖于具体的对象的其他方法。
接口是类型安全的, 代码更简短,性能更好。
尽量不要使用本地方法。
努力编写好的程序,性能会随之而来。
如果命名是一门学问,Java中有一些惯用法。
JVM没有对异常进行优化,异常控制的性能没有正常代码的性能好。
为了获知对象的状态,才能执行相应的操作, 有两种做法:
如果状态测试和状态相关方法是重复同样的工作, 出于性能考虑,使用返回值方法。 否则使用状态测试方法, 因为它更好理解。
只在可恢复的情况下使用受检异常。
对于受检异常,客户端catch住受检异常,然后执行恢复操作, 实现这注意发生异常时的回滚操作,使失败保持原子性。
对于非受检异常,实现者注释异常,异常的原因, 同时实现异常转译,异常中带有具体信息。 客户端根据异常的原因修改代码, 如果忽略异常,解释为什么要这样做。
待续。
要将一个类的对象序列化, 需要实现 readObject 和 writeObject 两个函数, 同时必要的时候需要实现 readResolve 这个方法。 readObject 注意实时保护性的拷贝, 对参数进行检查。 使用序列化代理来实现这种可能更加简单, 实现 writeReplace 这个方法就可以了。