高效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. 考虑序列化代理替代序列化实例

方法引用优于lambda表达式

Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化。
在这里第一时间翻译成中文版。供大家学习分享之用。

43.方法引用优于lambda表达式

lambda优于匿名类的主要优点是它更简洁。Java提供了一种生成函数对象的方法,比lambda还要简洁,那就是:方法引用( method references)。下面是一段程序代码片段,它维护一个从任意键到整数值的映射。如果将该值解释为键的实例个数,则该程序是一个多重集合的实现。该代码的功能是,根据键找到整数值,然后在此基础上加1:

map.merge(key, 1, (count, incr) -> count + incr);

请注意,此代码使用merge方法,该方法已添加到Java 8中的Map接口中。如果没有给定键的映射,则该方法只是插入给定值; 如果映射已经存在,则合并给定函数应用于当前值和给定值,并用结果覆盖当前值。 此代码表示merge方法的典型用例。

代码很好读,但仍然有一些样板的味道。 参数count和incr不会增加太多价值,并且占用相当大的空间。 真的,所有的lambda都告诉你函数返回两个参数的和。 从Java 8开始,Integer类(和所有其他包装数字基本类型)提供了一个静态方法总和,和它完全相同。 我们可以简单地传递一个对这个方法的引用,并以较少的视觉混乱得到相同的结果:

map.merge(key, 1, Integer::sum);

方法的参数越多,你可以通过方法引用消除更多的样板。 然而,在一些lambda中,选择的参数名称提供了有用的文档,使得lambda比方法引用更具可读性和可维护性,即使lambda看起来更长。

对于一个方法引用,你无能为力,因为你不能对lambda执行任何操作(只有一个难懂的异常 - 如果你好奇的话,参见JLS,9.9-2)。 也就是说,方法引用通常会导致更短,更清晰的代码。 如果lambda变得太长或太复杂,它们也会给你一个结果:你可以从lambda中提取代码到一个新的方法中,并用对该方法的引用代替lambda。 你可以给这个方法一个好名字,并把它文档记录下来。

如果你使用IDE编程,它将提供替换lambda的方法,并在任何地方使用方法引用。通常情况下,你应该接受这个提议。偶尔,lambda会比方法引用更简洁。这种情况经常发生在方法与lambda相同的类中。例如,考虑这段代码,它被假定出现在一个名为GoshThisClassNameIsHumongous的类中:

service.execute(GoshThisClassNameIsHumongous::action);

这个lambda类似于等价于下面的代码:

service.execute(() -> action());

使用方法引用的代码段既不比使用lambda的代码片段更短也不清晰,所以更喜欢后者。 在类似的代码行中,Function接口提供了一个通用的静态工厂方法来返回标识函数Function.identity()。 它通常更短,更清洁,而不使用这种方法,而是使用等效的lambda内联代码:x - > x

许多方法引用是指静态方法,但有四种方法没有。 其中两个是特定(bound)和任意(unbound)对象方法引用。 在特定对象引用中,接收对象在方法引用中指定。 特定对象引用在本质上与静态引用类似:函数对象与引用的方法具有相同的参数。 在任意对象引用中,接收对象在应用函数对象时通过方法的声明参数之前的附加参数指定。 任意对象引用通常用作流管道(pipelines)中的映射和过滤方法(条目 45)。 最后,对于类和数组,有两种构造方法引用。 构造方法引用用作工厂对象。 下表总结了所有五种方法引用:

方法引用类型 举例 等同的Lambda
Static Integer::parseInt str -> Integer.parseInt(str)
Bound Instant.now()::isAfter Instant then = Instant.now(); t -> then.isAfter(t)
Unbound String::toLowerCase str -> str.toLowerCase()
Class Constructor TreeMap<K,V>::new () -> new TreeMap<K,V>
Array Constructor int[]::new len -> new int[len]

总之,方法引用通常为lambda提供一个更简洁的选择。 如果方法引用看起来更简短更清晰,请使用它们;否则,还是坚持lambda