高效Java(第三版) Effective Java
1. 考虑使用静态工厂方法替代构造方法 2. 当构造方法参数过多时使用builder模式 3. 使用私有构造方法或枚类实现Singleton属性 4. 使用私有构造方法执行非实例化 5. 使用依赖注入取代硬连接资源 6. 避免创建不必要的对象 7. 消除过期的对象引用 8. 避免使用Finalizer和Cleaner机制 9. 使用try-with-resources语句替代try-finally语句 10. 重写equals方法时遵守通用约定 11. 重写equals方法时同时也要重写hashcode方法 12. 始终重写 toString 方法 13. 谨慎地重写 clone 方法 14. 考虑实现Comparable接口 15. 使类和成员的可访问性最小化 16. 在公共类中使用访问方法而不是公共属性 17. 最小化可变性 18. 组合优于继承 19. 如果使用继承则设计,并文档说明,否则不该使用 20. 接口优于抽象类 21. 为后代设计接口 22. 接口仅用来定义类型 23. 优先使用类层次而不是标签类 24. 优先考虑静态成员类 25. 将源文件限制为单个顶级类 26. 不要使用原始类型 27. 消除非检查警告 28. 列表优于数组 29. 优先考虑泛型 30. 优先使用泛型方法 31. 使用限定通配符来增加API的灵活性 32. 合理地结合泛型和可变参数 33. 优先考虑类型安全的异构容器 34. 使用枚举类型替代整型常量 35. 使用实例属性替代序数 36. 使用EnumSet替代位属性 37. 使用EnumMap替代序数索引 38. 使用接口模拟可扩展的枚举 39. 注解优于命名模式 40. 始终使用Override注解 41. 使用标记接口定义类型 42. lambda表达式优于匿名类 43. 方法引用优于lambda表达式 44. 优先使用标准的函数式接口 45. 明智审慎地使用Stream 46. 优先考虑流中无副作用的函数 47. 优先使用Collection而不是Stream来作为方法的返回类型 48. 谨慎使用流并行 49. 检查参数有效性 50. 必要时进行防御性拷贝 51. 仔细设计方法签名 52. 明智而审慎地使用重载 53. 明智而审慎地使用可变参数 54. 返回空的数组或集合不要返回null 55. 明智而审慎地返回Optional 56. 为所有已公开的API元素编写文档注释 57. 最小化局部变量的作用域 58. for-each循环优于传统for循环 59. 熟悉并使用Java类库 60. 需要精确的结果时避免使用float和double类型 61. 基本类型优于装箱的基本类型 62. 当有其他更合适的类型时就不用字符串 63. 注意字符串连接的性能 64. 通过对象的接口引用对象 65. 接口优于反射 66. 明智谨慎地使用本地方法 67. 明智谨慎地进行优化 68. 遵守普遍接受的命名约定 69. 仅在发生异常的条件下使用异常 70. 对可恢复条件使用检查异常,对编程错误使用运行时异常 71. 避免不必要地使用检查异常 72. 赞成使用标准异常 73. 抛出合乎于抽象的异常 74. 文档化每个方法抛出的所有异常 75. 在详细信息中包含失败捕获信息 76. 争取保持失败原子性 77. 同步访问共享的可变数据 78. 避免过度同步 79. EXECUTORS, TASKS, STREAMS 优于线程 80. 优先使用并发实用程序替代wait和notify 81. 线程安全文档化 82. 明智谨慎地使用延迟初始化 83. 不要依赖线程调度器 84. 其他替代方式优于Java本身序列化 85. 非常谨慎地实现SERIALIZABLE接口 86. 考虑使用自定义序列化形式 87. 防御性地编写READOBJECT方法 88. 对于实例控制,枚举类型优于READRESOLVE 89. 考虑序列化代理替代序列化实例

返回空的数组或集合不要返回null

Tips
书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code
注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

54. 返回空的数组或集合不要返回null

看到类似这样的方法并不罕见:

// Returns null to indicate an empty collection. Don"t do this!
private final List<Cheese> cheesesInStock = ...;

/**
 * @return a list containing all of the cheeses in the shop,
 *     or null if no cheeses are available for purchase.
 */
public List<Cheese> getCheeses() {
    return cheesesInStock.isEmpty() ? null
        : new ArrayList<>(cheesesInStock);
}

没有理由对没有奶酪(Cheese)可供购买的情况进行特殊处理。这样需要在客户端做额外的代码处理可能为null的返回值,例如:

List<Cheese> cheeses = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON))
    System.out.println("Jolly good, just the thing.");

在几乎每次使用返回null来代替空集合或数组的方法时,都需要使用这种迂回的方式。 它容易出错,因为编写客户端的程序员可能忘记编写特殊情况代码来处理null返回。 多年来这种错误可能会被忽视,因为这种方法通常会返回一个或多个对象。 此外,返回null代替空容器会使返回容器的方法的实现变得复杂。

有时有人认为,null返回值比空集合或数组更可取,因为它避免了分配空容器的开销。这个论点有两点是不成立的。首先,除非测量结果表明所讨论的分配是性能问题的真正原因,否则不宜担心此级别的性能(条目67)。第二,可以在不分配空集合和数组的情况下返回它们。下面是返回可能为空的集合的典型代码。通常,这就是你所需要的:

//The right way to return a possibly empty collection
public List<Cheese> getCheeses() {
    return new ArrayList<>(cheesesInStock);
}

如果有证据表明分配空集合会损害性能,可以通过重复返回相同的不可变空集合来避免分配,因为不可变对象可以自由共享(条目17)。下面的代码就是这样做的,使用了Collections.emptyList方法。如果你要返回一个Set,可以使用Collections.emptySet;如果要返回Map,则使用Collections.emptyMap。但是请记住,这是一个优化,很少需要它。如果你认为你需要它,测量一下前后的性能表现,确保它确实有帮助:

// Optimization - avoids allocating empty collections
public List<Cheese> getCheeses() {
    return cheesesInStock.isEmpty() ? Collections.emptyList()
        : new ArrayList<>(cheesesInStock);
}

数组的情况与集合的情况相同。 永远不要返回null,而是返回长度为零的数组。 通常,应该只返回一个正确长度的数组,这个长度可能为零。 请注意,我们将一个长度为零的数组传递给toArray方法,以指示所需的返回类型,即Cheese []

//The right way to return a possibly empty array
public Cheese[] getCheeses() {
    return cheesesInStock.toArray(new Cheese[0]);
}

如果你认为分配零长度数组会损害性能,则可以重复返回相同的零长度数组,因为所有零长度数组都是不可变的:

// Optimization - avoids allocating empty arrays

private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheeses() {
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);

}

在优化的版本中,我们将相同的空数组传递到每个toArray调用中,当cheesesInStock为空时,这个数组将从getCheeses返回。不要为了提高性能而预先分配传递给toArray的数组。研究表明,这样做会适得其反[Shipilev16]:

// Don’t do this - preallocating the array harms performance!

return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);

总之,永远不要返回null来代替空数组或集合。它使你的API更难以使用,更容易出错,并且没有性能优势。