Effective Java 2th Part III
Effective Java 2th Part III
通用程序设计
45、将局部变量的作用域最小化
- 第一次使用的地方声明
- 每个局部变量的声明都应该有一个初始化表达式
for(int i=0,j=getMax();i>j;i++)
使用这种方式避免每次迭代的冗余计算- 方法小而集中
46、for-each优先
- 没有性能损失(集合数只取了一次,见45条)
- 删除元素无法使用
- 转换元素无法使用
多个集合平行迭代无法使用
47、了解和使用类库
- Random.nextInt
48、需要精确的答案避免使用float和double
- BigDecimal,int(9位数字)或long(18位数字)
- int或long需要手动控制小数位数
- BigDecimal功能齐全但用法不方便,速度慢
49、基本类型优先于装箱基本类型
- ==操作符执行同一性比较
- 操作中混合装箱与基本类型,都会进行拆箱比较
- 循环中注意装箱类型反复计算导致的大量装箱拆箱操作1234Long sum=0L;for(long i=0L;i<1000000;i++){sum+=i;//装箱拆箱}
50、如果其他类型更适合,尽量避免使用字符串
- 不要代替其他类型,如int等数字形式,应当做类型转换
- 不适合代替枚举
- 不适合代替聚集类型(以分隔符表示一串有含义的信息”Name-kkk#Age-12”)
- 不适合代替能力表(用于授权的某个唯一变量标识)
51、当心字符串拼接性能
- 拼接字符串时间复杂度n平方
- 使用StringBuilder
52、通过接口引用对象
- 如果没有合适的接口存在,使用类引用
53、接口优先于反射机制
- 编译检查失效
- 代码冗长难懂
- 性能损失
54、谨慎的使用本地方法
- 性能逐渐不是问题
- 本地语言不是安全的
- 不可再自由移植
55、谨慎优化
- 不要进行优化
- 还是不要进行优化-在未找到清晰的优化方案之前
- 设计时要考虑性能(算法,API,数据格式,线路层)
- 避免限制性能的设计决策
- 优化之前与之后,测量性能
56、遵守普遍接受的命名惯例
- 详细内容见alibaba Java开发手册
异常
57、只针对异常的情况才使用异常
- 不要使用异常用于正常的控制流
58、对可恢复的情况使用受检异常,对编程错误使用运行时异常
- 不要使用error
- 未受检的抛出结构使用RuntimeException的子类
59、避免不必要地使用受检的异常
60、优先使用标准异常
61、抛出与抽象相对应的异常
- 高层的实现捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常
- 尽量处理或阻止来自底层的异常
62、每个方法抛出的异常要有文档
- 记录异常与条件
- 只记录未受检异常
63、在细节消息中包含能捕获失败的消息
- 异常信息应该包含所有对该异常有贡献的参数和值
- 可以通过构造特定的异常类构造函数去规范的产生高质量的帮助信息
64、努力使失败保持原子性
- 异常出现后,尽量使得处理的对象保持之前的状态而不要修改属性等信息
- 所有可能的检查在修改对象之前进行
65、不要忽略异常
- catch语句不要空置
并发
66、同步访问共享的可变数据
- 注意”活性失败”, JVM优化可能将局部变量修改读取方式导致无法看到关键的开关变量
- 读写都需要同步
- “安全性失败”,常规的并发问题,如i++
67、避免过度同步
- 同步区域尽可能小
- 同步区域不可调用外来方法(接口,可覆盖的方法等),会导致死锁或异常
68、Executor和task优于线程
- 使用线程池而不是简单的new Thread(){}.start();
69、并发工具优先于wait和notify
- 永远不要在循环之外调用o.wait()
- 新代码中不要使用wait和notify,而是用并发工具替代(Executor,并发集合,同步器)
- 优先使用notifyAll();
70、线程安全文档化
- 不同级别的线程安全要有文档说明
- 锁封装在同步的对象中
71、慎用延迟初始化
静态域使用内部类实现
1234567891011public class A{private static class B{static final Object field=getField();}private A(){};public Object getField(){return B.field;}}实例域使用双重检查
12345678910111213Object getField(){Object res=field;if(null==field){synchronized(this){res=field;if(null==res){field=res=getField();}}}return res;}
72、不要依赖于线程调度器
- Thead.yield 不靠谱,只用于手动的增加并发用以复现测试bug
- 线程优先级不靠谱
73、避免使用线程组
- 禁用
序列化
74、谨慎地实现Serializable接口
- 大大降低修改灵活性
- serialVersionUID如果不提供,系统会调用复杂运算过程生成
- 序列化构造器覆盖了默认的构造器行为
- 新版本发布,测试负担加重
- 为继承而设计的类尽量少的实现序列化接口
- 使用AtomicReference实现线程安全状态机表示类已经初始化等信息
- 内部类不要实现(内部类有另外的合成域用于指向外部类成员,序列化形式不清楚)
- 静态成员类可以实现
75、考虑使用自定义的序列化形式
- 如果对象的物理表示法和逻辑数据内容有实质性的区别,不建议使用默认序列化形式
- 加序列化UID(性能提升一些)
76、保护性的编写readobject方法
- readObject方法中尽量对字段进行保护性拷贝后赋值,序列化字节流可能存在对对象内部字段的引用,进而再反序列化之后调用readObject之后对内部字段进行修改
77、对于实例控制,枚举类型优先于readResolve
- 对于单例模式实例化,使用枚举类型作为实例控制
78、考虑用序列化代理代替序列化实例