我们学习一门语言,包括编程语言,都要经过一些阶段,入门,熟练 精通。每个人在每个阶段耗费的时间不同。如何从一
个入门级别继续提高,除了实践,吸取一些前人经验是必须了。《Effective Java》将一些Java的高级主题一一列举,如
庖丁解牛般。是入门级Java Programmer 继续提高水平不可多得的资料。这里分析我的一些读书笔记吧。
----------------------------------------------------------------------------------------------------------------------
创建和销毁对象
<!--[if !supportLists]-->1. <!--[endif]-->优先考虑工厂方法而不是构造函数。
优点:
a.工厂方法是有名字的,方便代码调用。
b.跟构造函数不同,它不需要每次都new一个实例。
c.工厂方法可以返回子类型。
d.工厂方法减少创建实例时传递参数的冗余
不足:
a.使用了工厂方法,势必使得构造函数private,从而使该类无法被继承。
b.工厂方法与其他的静态方法无法区分,只能通过一个命名规范来区分
2.当构造参数很多时,考虑使用builder
传统的方法是使用多个构造函数,或者用JavaBean的set方法。这些都不够优雅。
Builder 采用一个内部类来帮助构造,类似链式构造,最后再调用真正的构造函数。
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100). build();
<!--[if !supportLists]-->3. <!--[endif]-->通过枚举类型或者使构造函数私有来达到单例
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
<!--[if !supportLists]-->4. <!--[endif]-->防止被实例化,只需要使构造函数 private
<!--[if !supportLists]-->5. <!--[endif]-->避免创建不必要的对象
例如 String s = new String(“hello”); è String s = “hello”;
<!--[if !supportLists]-->6. <!--[endif]-->当一个对象不再被引用时,set 为 null,消除引用,使得它可被gc回收
<!--[if !supportLists]-->7. <!--[endif]-->避免使用 finallizers
无法预知finallizers 合适被调用,或者根本就不被调用,在性能上也有很多损害。
所以对象的公共方法
8.当要override equals,遵循一些基本原则
a. 类的每个实例都是不同的。
b.一个类是 private 或者 package-private,确保equals不会被调用。
Equals 要保证 自反,对称,传递,持久
良好的equals通常是这样的。先检测要比较的实例是否==this,然后再进行类型检验,接着进行类型转换,最后自定义如何实现equals
<!--[if !supportLists]-->9. <!--[endif]-->override equals 时总是要记得override hashCode
常用的计算公式:result = 31 * result + c;
<!--[if !supportLists]-->10. <!--[endif]-->always override toString
11. 谨慎override clone
尽量用copy 。记得先调用父类的clone
<!--[if !supportLists]-->12. <!--[endif]-->考虑实现 comparable
类和接口
<!--[if !supportLists]-->13. <!--[endif]-->减少 类和成员 的可访问范围
<!--[if !supportLists]-->14. <!--[endif]-->类的成员不要用public,改为private,提供一个方法来访问
<!--[if !supportLists]-->15. <!--[endif]-->减少类的可变性 mutability
<!--[if !supportLists]-->a. <!--[endif]-->不要提供方法来改变对象的状态
<!--[if !supportLists]-->b. <!--[endif]-->确保类不被继承
<!--[if !supportLists]-->c. <!--[endif]-->所有的field都是final
<!--[if !supportLists]-->d. <!--[endif]-->所有的field都是private
<!--[if !supportLists]-->e. <!--[endif]-->不引用其他的可变mutable 变量
不可变的类天生就是线程安全的
<!--[if !supportLists]-->16. <!--[endif]-->优先采用组合,而非继承
<!--[if !supportLists]-->17. <!--[endif]-->如果一个类需要被继承,仔细设计并且文档化,否则就禁止继承(final 修饰或者 构造函数private )
<!--[if !supportLists]-->18. <!--[endif]-->优先考虑interface 而不是 抽象类
<!--[if !supportLists]-->19. <!--[endif]-->接口不要用来定义常量,而是用来定义类型的
<!--[if !supportLists]-->20. <!--[endif]-->使用多态来消除标志。
很多时候,我们在一个类中定义了很多行为。每次根据类的不同属性,就调用不同的行为。这是ugly 的。应该重新设计你的类,用多态来消除
21.使用 function object 来实现 策略 strategies
22.优先采用 static member 而不是 nonstatic
泛型
23.Java1.5后,就要用泛型,不要再用 raw type 了
可以在编译期就检查,不需要类型转换
24. 消除各种 unchecked warnings
25.优先采用list ,而不是 array
Array类型是是固定不变的
26.定义类型时,采用泛型类型,更容易扩展
27.采用 泛型方法,适用范围更广
28.使用 边界通配符 可以增加API的灵活性
有一个原则 PECS stands for producer-extends, consumer-super.
29.考虑使用类型安全的容器
枚举和注解
30.将int常量都改为 enum
31.需要用到的 ordinal,用实例的一个属性来替换 因为 ordinal 是可变(人为改变顺序)
32.将 标志变量 换成 EnumSet
33.将顺序的下标换成 EnumMap
34.Emulate extensible enums with interfaces
35.相对于命名,annotation 更合适
命名容易出错
36.坚持使用 @Override ,可以在编译期就帮忙检查
37.Use marker interfaces to define types
marker interfaces就是一个空接口,没有方法的。类似 Serializable
方法
38.检验参数的正确
39.必要时采用 防御式拷贝 defensive copy
40.仔细设计方法(命名,参数,类型……)
41.谨慎使用方法重载,很多时候用不同的方法名来区分更合适
42.谨慎使用 varargs,varargs 可以是 任意数量的参数(包括0)
43. 返回空的array 或者 集合,而不是null
44.为每个暴露的API元素 编写文档
General Programming
45.减少 本地变量的 作用域,用到时才定义
46.多使用 for – each 而不是传统的 for,代码更优雅
47.熟悉和使用 类库
48.避免使用 float 和 double ,尤其是你需要精确答案。可以采用扩大倍数的 int、long 或者 BigDecimal
49. 优先使用 primitive 而不是 装箱后 的 primitive
50.如果有其他合适的类型,不要用string
51.当心使用 String 的 +,用 StringBuffer(线程安全),StringBuilder 代替
52.定义类型时,用类型、接口来定义,而不是具体类
53.优先使用interface而不是reflection
使用reflection,有一些缺点:丧失编译期的类型检查,代码不够优雅,性能考虑
54.谨慎使用 native method,可以用java重新实现
55.谨慎优化,Strive to write good programs rather than fast ones.
56.坚持一些好的命名规范 。可以参考我的读书笔记《Clean Code》
异常
57.只在可能出现异常的情况下才使用异常处理
58.对可恢复的情况使用 checked exception ,对于程序运行错误使用 runtime exception
59.避免 不必要的checked exception
60.优先使用标准的异常
61.Throw exceptions appropriate to the abstraction
适用包装exception
62. 在抛异常的方法中doc你的异常。
63.捕获异常时,尽快提供详细的信息
64.保持 错误的原子性,不会导致对象状态的不一致
65.不要捕获异常,就直接忽略,起码要有个注释吧
并发
66.Mutable 可变数据的访问要加 synchronize
a. 保证可见性, b.防止数据冲突
67.避免过度的 synchronization
68.采用 executors 和 task
69.尽量用 高级的concurrency 组件来替换 wait 和 notify
70.文档中注明线程的安全性(是否线程安全,什么条件下才线程安全)
71.谨慎使用延迟初始化 (Java中典型的单例)
72.不要依赖于线程调度,例如 Thread.yield()
73.避免使用 thread group
序列化
74.谨慎实现 Serialization
75.考虑使用本地化的serialized 方式
76.readObject方法,用防御性拷贝
77.为了实例控制,优先使用 enum type,而不是readResolve
78.考虑使用 系列化代理,而不是直接系列化实例。