最小化局部变量的作用域
- 在首次使用局部变量的地方声明它
for-each 循环优于传统 for 循环
有三种常见的情况是你不能分别使用 for-each 循环的:
- 有损过滤(Destructive filtering)——如果需要遍历集合,并删除指定选元素,则需要使用显式迭代器,以便可以调用其 remove 方法。 通常可以使用在 Java 8 中添加的 Collection 类中的 removeIf 方法,来避免显式遍历。
- 转换——如果需要遍历一个列表或数组并替换其元素的部分或全部值,那么需要列表迭代器或数组索引来替换元素的值。
- 并行迭代——如果需要并行地遍历多个集合,那么需要显式地控制迭代器或索引变量,以便所有迭代器或索引变量都可以同步进行
若需要精确答案就应避免使用 float 和 double 类型,尤其是货币运算
- 使用 BigDecimal、int 或 long 进行货币计算总之,对于任何需要精确答案的计算,不要使用 float 或 double 类型。
- 如果希望系统来处理十进制小数点,并且不介意不使用基本类型带来的不便和成本,请使用 BigDecimal。
- 使用 BigDecimal 的另一个好处是,它可以完全控制舍入,当执行需要舍入的操作时,可以从八种舍入模式中进行选择。
- 如果你使用合法的舍入行为执行业务计算,这将非常方便。如果性能是最重要的,那么你不介意自己处理十进制小数点,而且数值不是太大,可以使用 int 或 long。如果数值不超过 9 位小数,可以使用 int;如果不超过 18 位,可以使用 long。如果数量可能超过 18 位,则使用 BigDecimal。
谨慎优化
- 不要努力写快的程序,要努力写好程序;速度自然会提高。但是在设计系统时一定要考虑性能,特别是在设计API、线路层协议和持久数据格式时。
- 当完成了系统的构建之后,请度量它的性能。如果足够快,就完成了。如果没有,利用分析器找到问题的根源,并对系统的相关部分进行优化。第一步是检查算法的选择:再多的底层优化也不能弥补算法选择的不足。根据需要重复这个过程,在每次更改之后测量性能,直到你满意为止。
异常不要乱用
- 异常应该只用于异常的情况下;他们永远不应该用于正常的程序控制流程。
- 比如使用异常来作为循环的退出条件,这种基于异常的循环模式不仅模糊了代码的意图,降低了它的性能,而且它还不能保证正常工作!如果出现了不相关的 bug,这个模式会悄悄的消失从而掩盖了这个 Bug,极大地增加了调试过程的复杂性。
异常使用
- 对于可恢复的情况,比如某一时刻系统访问量增大,内存不足,要抛出受检异常;
- 对于程序错误,比如内存泄漏,就要抛出运行时异常。
- 不确定是否可恢复,就抛出为受检异常。不要定义任何既不是受检异常也不是运行异常的抛出类型。要在受检异常上提供方法,以便协助程序恢复。
- 如果不能阻止或者处理来自更低层的异常,一般的做法是使用异常转译,转译成高层适用的异常,只有在低层方法的规范碰巧可以保证“它所抛出的所有异常对于更高层也是合适的”情况下,才可以将异常从低层传播到高层。
- 要为你编写的每个方法所能抛出的每个异常建立文档。
- 为异常的失败- 捕获信息,比如数组越界时的上界,下界和当前位置index。
失败的原子性
- 失败的方法调用应该使对象保持在被调用之前的状态
同步异步
- 当多个线程共享可变数据的时候,每个读或者写数据的线程都必须执行同步。 如果没有同步,就无法保证一个线程所做的修改可以被另一个线程获知。未能同步共享可变数据会造成程序的活跃性失败( liveness failure )和安全性失败( safety failure ) 。
- 为了避免活性失败和安全性失败,在一个被同步的方法或者代码块中,永远不要放弃对客户端的控制。 就是不要在需要同步的代码中使用到调用者提供的方法 。从包含该同步区域的类的角度来看,这样的方法是外来的( alien ) 。这个类不知道该方法会做什么事情,也无法控制它。根据外来方法的作用,从同步区域中调用它会导致异常、死锁或者数据损坏。
- 应该在同步区域内做尽可能少的工作
- 多使用executor 、task 和 stream 而不是线程
避免使用序列化
- 序列化是危险的,应该避免。如果你从头开始设计一个系统,可以使用跨平台的结构化数据,如 JSON 或 protobuf。不要反序列化不可信的数据。如果必须这样做,请使用对象反序列化过滤,但要注意,它不能保证阻止所有攻击。避免编写可序列化的类。如果你必须这样做,一定要非常小心。
- 如果非要实现序列化,那么有很多缺陷
- 实现 Serializable 接口的一个主要代价是,一旦类的实现被发布,它就会降低更改该类实现的灵活性。
- 增加了出现 bug 和安全漏洞的可能性
- 增加了与发布类的新版本相关的测试负担
- 去实现Serializable 接口需要考虑的很多
- 为继承而设计的类很少情况适合实现 Serializable 接口,接口也很少情况适合扩展它
- 内部类不应该实现 Serializable,静态成员类可以实现 Serializable 接口
参考:
-
《effective java》3rd
-- Joshua Bloch
网友评论