《Effect Java》 归纳总结
目录:
一、创建和销毁对象 (1 ~ 7)
二、对于所有对象都通用的方法 (8 ~ 12)
三、类和接口 (13 ~ 22)
四、泛型 (23 ~ 29)
五、枚举和注解 (30 ~ 37)
六、方法 (38 ~ 44)
七、通用程序设计 (45 ~ 56)
八、异常 (57 ~ 65)
九、并发 (66 ~ 73)
十、序列化 (74 ~ 78)
正文:
第一章: 创建和销毁对象
1、考虑用静态工厂方法代替构造器
优:
① 有名称
② 不必在每次调用它们的时候都创建一个对象
③ 可以放回原返回类型的任何子类型的对象
④ 在创建参数化类型实例的时候,代码更简洁
缺:
① 类如果不含公有的或受保护的构造器,就不能被子类化
② 与其他一般的静态方法无多大区别
2、 遇到多个构造器参数时要考虑用构造器
3、用私有构造器或者枚举类型强化 Singleton 属性
4、通过私有构造器强化不可实例化的能力
5、避免创建不必要的对象
6、消除过期的对象引用
7、避免使用终结方法
第二章: 对于所有对象都通用的方法
8、覆盖 equals 时请遵守通用约定
① 类的每个实例本质上是唯一的
② 不关心类是否提供了“逻辑相等”的测试功能
③ 超类已经覆盖了 equals, 从超类继承过来的行为对于子类也是合适的
④ 类是私有的或包级私有的,可以确定它的 equals 方法永远不会被调用
实现高质量 equals 方法诀窍:
① 使用 “==”操作符检查“参数是否为这个对象的引用”
② 使用 instanceof 操作符检查“参数是否为正确的类型”
③ 把参数转换为正确的类型
④ 对于该类中的每个“关键”域,检查参数中的域是否参与该对象中的对应的域相匹配
⑤ 当你编写完成了 equals 方法之后,应该判断其是否满足了 对称性、传递性、一致性
-
-
- 覆盖 equals 时总要覆盖 hashCode
- 不要企图让 equals 方法过于智能
- 不要将 equals 声明中的 Object 对象器替换为其他的类型
-
9、覆盖 equals 时总要覆盖 hashCode
10、始终要覆盖 toString
11、谨慎地覆盖 clone
12、考虑实现 Comparable 接口
第三章:类和接口
13、使类和成员的可访问性最小化
14、在公有类中使用访问方法而非公有域
15、使可变性最小化
① 不要提供任何会修改对象状态的方法
② 保证类不会被扩展
③ 使所有的域都是 final 的
④ 使所有的域都成为私有的
⑤ 确保对于任何可变组件的互斥访问
16、复合优先于继承
简而言之,继承的功能非常强大,但是也存在诸多问题,因为它违背了封装原则。只有当子类和超类之间确实存在子类类型关系时,使用继承才是恰当的。即便如此,如果子类和超类处在不同的包中,并且超类并不是为了继承而设计的,那么继承将会导致脆弱性。为了避免,可以用复合和转发机制来代替继承,尤其是当存在适合的接口可以实现包装类的时候。包装类不仅比子类更加健壮,而且功能也更加强大。
17、要么为继承而设计,并提供文档说明,要么就禁止继承
18、接口优于抽象类
① 现有的类可以很容易被更新,以实现新的接口
② 接口的定义的理想选择
③ 接口允许我们构造非层次结构的类型框架
接口通常是定义允许多个实现的类型的最佳途径。这条规律有个例外,即当演变的容易性比灵活性和功能更为重要的时候。在这种情况下,应该使用抽象类来定义类型,但前提是必须理解并且可以接受这些局限性。如果你导出了一个重要的接口,就应该坚决考虑同时提供骨架实现类。最后,应该尽可能谨慎的设计所有的公有接口,并通过编写多个实现类对它们进行全面的测试。
19、接口只用于定义类型
接口应该只被用来定义类型,它们不应该被用来导出常量。
20、类层次优于标签类
标签类很少有适用的时候。当你想要编写一个包含显示标签域的类时,应该考虑一下,这个标签是否可以被取消,这个类是否可以用类层次来代替。当你遇到一个包含标签域的现有类时,就要考虑将它重构到一个层次结构中去。
21、用函数对象表示策略
要声明一个接口来表示该策略,并且为每个具体策略声明一个实现了该接口的类。当一个具体策略只被使用一次时,通常实现匿名类来声明和实例化这个具体策略类。当一个具体策略类是设计用来重复使用的时候,它的类通常就要被实现为私有的静态成员类,并通过公有的静态 final 域被导出,其类型为该策略接口。
22、优先考虑静态成员类
静态成员类、非静态成员类、匿名类、局部类
① 如果一个嵌套类需要在单个方法之外仍然是可见的,或者他太长了,不适合于放在内部,就应该使用成员类。
② 如果成员类的每个实例都需要一个指向其外围实例的引用,就要把成员类做成非静态的;否则就做成静态的。
③ 假设这个嵌套类属于一个方法的内部,如果你只需要在一个地方创建实例,并且已经有一个预置的类型可以说明这个类的特征,就要把它做成匿名类;否则,就做成局部类。
第四章 泛型
23、请不要在新代码中使用原生态类型
24、消除非受检警告
每一条警告都表示可能在运行时片抛出 ClassCastException 异常。要尽最大的努力消除这些警告。如果无法消除非受检警告,同时可以证明引起警告的代码是类型安全的,就可以在尽可能小的范围中,用 @SuppressWarnings("unchecked")注释禁止该警告。要用注释把禁止该警告的原因记录下来。
25、列表优先于数组
数组和泛型有着非常不同的类型规则。数组是协变且可以具体化的;泛型是不可变的且可以被擦除的。因此数组提供了运行时的类型安全,当时没有编译时的类型安全,反之,对于泛型也一样。一般来说,数组和泛型不能很好地混合使用。如果你发现自己将它们混合起来使用,并且得到了编译时错误或警告,应该用列表代替数组。
26、优先考虑泛型
27、优先考虑泛型方法
28、利用有限制通配符来提升API 的灵活性
29、优先考虑类型安全的异构容器
第六章 枚举和注解
30、用 enum 代替 int 常量
31、用实例域代替序数
32、用 EnumSet 代替位域
33、用 EnumMap 代替序数索引
34、用接口模拟可伸缩的枚举
35、注解优先于命名模式
36、坚持使用 Override 注解
37、用标记接口定义类型
第七章 方法
38、检查参数的有效性
39、必要时进行保护性拷贝
40、谨慎设计方法签名
① 谨慎地选择方法的名称
② 不要过于追求提供便利的方法
③ 避免过长的参数列表
41、慎用重载
“能够重载方法”并不意味着就“应该重载方法”。一般情况下,对于多个具有相同参数数目的方法来说,应该尽量避免重载方法。在某些情况下,特别是涉及构造器的时候,要遵循这条建议也许是不可能的。在这种情况下,至少应该避免这样的情形:同一组参数只需经过类型转换就可以被传递给不同的重载方法。
42、慎用可变参数
43、返回零长度的数组或者集合,而不是 null
44、未所有导出的 API 元素编写文档注释
第八章 通用程序设计
45、将局部变量的作用域最小化
46、for-each 循环优先于传统的 for 循环(除了 在过滤、转换、平行迭代下外)
47、了解和使用类库
48、如果需要精确的答案,请避免使用 float 和 double
49、基本类型优先于装箱基本类型
50、如果其他类型更适合,则尽量避免使用字符串
① 字符串不适合代替其他的值类型
② 字符串不适合代替枚举类型
③ 字符串不适合代替聚集类型
④ 字符串不适合代替能力表
51、当心字符串连接的性能
52、通过接口引用对象
53、接口优先于反射机制
反射的不足:
① 丧失了编译时类型检查的好处
② 执行反射访问所需要的代码非常笨拙和冗长
③ 性能损失
54、谨慎使用本地方法
55、谨慎地进行优化
56、遵守普遍接受的命名惯例
第九章 异常
57、只针对异常的情况才使用异常
58、对于可恢复的情况使用受检异常,对于编程错误使用运行时异常
59、避免不必要地使用受检的异常
60、优先使用标准的异常
61、抛出与抽象相对应的异常
62、每个方法抛出的异常都要有文档
63、在细节消息中包含能捕获失败的信息
64、努力使失败保持原子性
65、不要忽略异常
第十章 并发
66、同步访问共享的可变数据
67、避免过度同步
为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法。更为一般地讲,要尽量限制同步区域内部的工作量。
68、executor 和 task 优先于线程
69、并发工具优先于 wait 和 notify
70、线程安全性的文档化
71、使用延迟初始化
72、不要依赖于线程调度器
73、避免使用线程组
第十一章 序列化
74、谨慎地实现 Serializable 接口
使用此接口代价:
① 一旦一个雷被发布,就大大降低了“改变这个类的实现”的灵活性
② 增加了出现 Bug和安全漏洞的可能性
③ 随着类发行新的版本,相关的测试负担也增加了
75、考虑使用自定义的序列化形式
使用默认序列化形式缺点:
① 它使这个类的导出 API 永远地束缚在该类的内部表示法上
② 它会消耗过多的空间
③ 它会消耗过多的时间
④ 它会引起栈溢出
76、保护性地编写 readObject 方法
77、对于实例控制,枚举类型优先于 readResolve
78、考虑用序列化代理代替序列化实例
************** 分享技术知识,分享快乐喜悦*******************