方法
检查参数的有效性
绝大多数方法和构造器对于传递给它们的参数值都会有某些限制。例如,索引值必须是非负数,对象引用不能为null,等等,这些都是很常见的。对于参数的校验,可以使用 @NotNull
必要时进行保护性拷贝
谨慎设计方法签名
慎用重载
在Java中,参数类型或者个数不一样,对返回参数没有要求,叫做重载。
慎用可变参数
可变参数如下代码所示:
1 | static int sum(int... args) { |
在定义参数数目不定的方法时,可变参数方法是一种很方便的方式,但是它们不应该被过度滥用。如果使用不当,会产生混乱的结果。
返回零长度的数组或者集合,而不是null
返回类型为数组或集合的方法没理由返回null,而不是返回一个零长度的数组或者集合。
为所有导出的API元素编写文档注释
方便后来者阅读以及使用 API 的用户使用。
通用程序设计
将局部变量的作用域最小化
要使局部变量的作用域最小化,最有利的方法就是在第一次使用它的地方声明。如果变量在使用之前声明,这只会造成混乱——对于试图理解程序功能的读者来说,多了一种分散注意力的因素。等到用到该变量的时候,读者可能已经记不起该变量的类型或者初始值了。
for-each循环优先于传统的for循环
这个就是推荐使用下面这种循环:
1 | for(Element e: elements){ |
对于这种循环,它的性能比传统的循环,性能更好。for-each循环在简洁性和预防Bug方面有着传统的for循环无法比拟的优势,并且没有性能损失。应该尽可能地使用for-each循环。不过在Java8中,stream 有一种foreach功能,可以试试使用。
1 | List<Integer> list = new ArrayList<>(); |
了解和使用类库
Java提供了很多的标准库,我们在平时使用的时候,不需要再去造轮子了,可以直接使用,但是在使用的时候,需要注意这些类库的一些使用情况。
如果需要精确的答案,请避免使用 float和 double
如果性能非常关键,并且你又不介意自己记录十进制小数点,而且所涉及的数值又不太大,就可以使用int或者long。如果数值范围没有超过9位十进制数字,就可以使用int;如果不超过18位数字,就可以使用long。如果数值可能超过18位数字,就必须使用 BigDecimal。
进行货币计算,正确的办法是使用 BigDecimal、int 或者 long。
基本类型优先于装箱基本类型
基本类型简单,在运行的时候,速度更加快。装箱基本类型在创建的时候,初始是null。
如果其他类型更适合,则尽量避免使用字符串
- 字符串不适合替代其他的值类型
- 字符串不适合替代枚举类型
- 字符串不适合替代聚合类型
- 字符串不适合替代能力表
当心字符串连接的性能
可以查看String,StringBuilder,StringBuffer源码分析。
通过接口引用对象
接口优先于反射机制
谨慎地使用本地方法
本地方法(native method),指的是用本地程序设计语言(比如 C 或者 C++)来编写的特殊方法,有 native 修饰的。
谨慎地进行优化
遵守普遍接受的命名惯例
异常
只针对异常的情况才使用异常
对于程序,能够通过代码解决的问题,不要丢给异常来处理。
对可恢复的情况使用受检异常,对编程错误使用运行时异常
避免不必要地使用受检的异常
优先使用标准的异常
抛出与抽象相对应的异常
每个方法抛出的异常都要有文档
使用 Javadoc 的 @throws 标签记录下一个方法可能拋出的每个未受检异常,但是不要使用 throws 关键字将未受检的异常包含在方法的声明中。
在细节消息中包含能捕获失败的信息
努力使失败保持原子性
不要忽略异常
对于异常,需要解决。
并发
同步访问共享的可变数据
当多个线程共享可变数据的时候,每个读或者写数据的线程都必须执行同步。
避免过度同步
这会导致程序整体性能下降。为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法。更为一般地讲,要尽量限制同步区域内部的工作量。当你在设计一个可变类的时候,要考虑一下它们是否应该自己完成同步操作。在现在现在多核的时代,这比永远不要过度同步来得更重要。
executor和task优先于线程
使用线程池。多线程编程 —— 线程池
并发工具优先于 wait 和 notify
在 java.util.concurrent
工具包下,提供了一些并发工具类,比如 CountDownLatch 和 Semaphore。可以参考CyclicBarrier、CountDownLatch 和 Semaphore 的比较
线程安全性的文档化
慎用延迟初始化
不要依赖于线程调度器
避免使用线程组
序列化
谨慎地实现 Serializable 接口
实现 Serializable 接口而付出的最大代价是,一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性。如果一个类实现 Serializable 接口,它的字节流编码就变成了它的导出 API 的一部分。如果使用这个类,就必须支持这种序列化。
第二个代价是增加了出现 Bug 和安全漏洞的可能性。
第三个代价是随着类的发行新的版本,相关的测试负担也增加了。
PS:对于这一点,不是很明白。