0%

Effective Java 笔记三

方法

检查参数的有效性

绝大多数方法和构造器对于传递给它们的参数值都会有某些限制。例如,索引值必须是非负数,对象引用不能为null,等等,这些都是很常见的。对于参数的校验,可以使用 @NotNull

必要时进行保护性拷贝

谨慎设计方法签名

慎用重载

在Java中,参数类型或者个数不一样,对返回参数没有要求,叫做重载。

慎用可变参数

可变参数如下代码所示:

1
2
3
4
5
6
7
8
static int sum(int... args) {
int sum = 0;
for (int arg : args) {
sum += arg;
}

return sum;
}

在定义参数数目不定的方法时,可变参数方法是一种很方便的方式,但是它们不应该被过度滥用。如果使用不当,会产生混乱的结果。

返回零长度的数组或者集合,而不是null

返回类型为数组或集合的方法没理由返回null,而不是返回一个零长度的数组或者集合。

为所有导出的API元素编写文档注释

方便后来者阅读以及使用 API 的用户使用。

通用程序设计

将局部变量的作用域最小化

要使局部变量的作用域最小化,最有利的方法就是在第一次使用它的地方声明。如果变量在使用之前声明,这只会造成混乱——对于试图理解程序功能的读者来说,多了一种分散注意力的因素。等到用到该变量的时候,读者可能已经记不起该变量的类型或者初始值了。

for-each循环优先于传统的for循环

这个就是推荐使用下面这种循环:

1
2
3
for(Element e: elements){
doSomething (e);
}

对于这种循环,它的性能比传统的循环,性能更好。for-each循环在简洁性和预防Bug方面有着传统的for循环无法比拟的优势,并且没有性能损失。应该尽可能地使用for-each循环。不过在Java8中,stream 有一种foreach功能,可以试试使用。

1
2
3
4
List<Integer> list = new ArrayList<>();
list.forEach(input->{
//对数组的操作
});

了解和使用类库

Java提供了很多的标准库,我们在平时使用的时候,不需要再去造轮子了,可以直接使用,但是在使用的时候,需要注意这些类库的一些使用情况。

如果需要精确的答案,请避免使用 float和 double

如果性能非常关键,并且你又不介意自己记录十进制小数点,而且所涉及的数值又不太大,就可以使用int或者long。如果数值范围没有超过9位十进制数字,就可以使用int;如果不超过18位数字,就可以使用long。如果数值可能超过18位数字,就必须使用 BigDecimal。

进行货币计算,正确的办法是使用 BigDecimal、int 或者 long。

基本类型优先于装箱基本类型

基本类型简单,在运行的时候,速度更加快。装箱基本类型在创建的时候,初始是null。

如果其他类型更适合,则尽量避免使用字符串

  • 字符串不适合替代其他的值类型
  • 字符串不适合替代枚举类型
  • 字符串不适合替代聚合类型
  • 字符串不适合替代能力表

当心字符串连接的性能

可以查看String,StringBuilder,StringBuffer源码分析

通过接口引用对象

接口优先于反射机制

谨慎地使用本地方法

本地方法(native method),指的是用本地程序设计语言(比如 C 或者 C++)来编写的特殊方法,有 native 修饰的。

谨慎地进行优化

遵守普遍接受的命名惯例

可以参考阿里巴巴的Java手册

异常

只针对异常的情况才使用异常

对于程序,能够通过代码解决的问题,不要丢给异常来处理。

对可恢复的情况使用受检异常,对编程错误使用运行时异常

避免不必要地使用受检的异常

优先使用标准的异常

抛出与抽象相对应的异常

每个方法抛出的异常都要有文档

使用 Javadoc 的 @throws 标签记录下一个方法可能拋出的每个未受检异常,但是不要使用 throws 关键字将未受检的异常包含在方法的声明中。

在细节消息中包含能捕获失败的信息

努力使失败保持原子性

不要忽略异常

对于异常,需要解决。

并发

同步访问共享的可变数据

当多个线程共享可变数据的时候,每个读或者写数据的线程都必须执行同步。

避免过度同步

这会导致程序整体性能下降。为了避免死锁和数据破坏,千万不要从同步区域内部调用外来方法。更为一般地讲,要尽量限制同步区域内部的工作量。当你在设计一个可变类的时候,要考虑一下它们是否应该自己完成同步操作。在现在现在多核的时代,这比永远不要过度同步来得更重要。

executor和task优先于线程

使用线程池。多线程编程 —— 线程池

并发工具优先于 wait 和 notify

java.util.concurrent 工具包下,提供了一些并发工具类,比如 CountDownLatch 和 Semaphore。可以参考CyclicBarrier、CountDownLatch 和 Semaphore 的比较

线程安全性的文档化

慎用延迟初始化

不要依赖于线程调度器

避免使用线程组

序列化

谨慎地实现 Serializable 接口

实现 Serializable 接口而付出的最大代价是,一旦一个类被发布,就大大降低了“改变这个类的实现”的灵活性。如果一个类实现 Serializable 接口,它的字节流编码就变成了它的导出 API 的一部分。如果使用这个类,就必须支持这种序列化。

第二个代价是增加了出现 Bug 和安全漏洞的可能性。

第三个代价是随着类的发行新的版本,相关的测试负担也增加了。

PS:对于这一点,不是很明白。

考虑使用自定义的序列化形式

保护性地编写 readobject 方法

对于实例控制,枚举类型优先于 readResolve

考虑用序列化代理代替序列化实例

客官,赏一杯coffee嘛~~~~