0%

Effective Java 第三版 (8) 通用程序设计

通用程序设计

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

要使局部变量作用域最小化,最有力的方法就是在第一次使用它的地方声明。

for 循环比 while 循环更好,因为 for 循环中,可以使用局部作用域。

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

对于集合的首选遍历做法使用 for-each 循环。不会有性能损失,甚至某些情况还稍有性能优势(因为对数组索引的边界只计算一次)。在简洁性和预防Bug 方面有绝对优势。

1
2
for (Element e : elements) {
}

当看到冒号(:) 时,请将其读作「in」,因此,上面的循环读作「对于元素 elements 中的每个元素 e」

三种常见的情况无法使用 for-each 循环:

  • 过滤。遍历集合,并删除指定的元素。
  • 转换。遍历列表或数组,取代它部分或者全部元素值
  • 并行迭代。需要并行地遍历多个集合。

§ 了解和使用类库

从 Java 7 开始,就不应该再使用 Random

在大多数情况下,选择的随机数生成器现在是 ThreadLocalRandom。 它能产生更高质量的随机数,而且速度非常快。对于 fork 连接池和并行流,使用 SplittableRandom

通过使用标准库,你可以利用编写它的专家的知识和以前使用它的人的经验。

每个程序员都应该熟悉 java.langjava.utiljava.io 中的内容。

Collections 框架和 Streams 也应当是每个程序员基本包的一部分。

java.util.concurrent 也是其中一部分。

总而言之,不要白费力气重新发明轮子。如果你需要做一些看起来相当常见的事情,那么库中可能已经有一个工具可以做你想做的事情。

如果有,使用它,如果你不知道,检索一下。

一般来说,库代码可能比你自己编写的代码更好,并且随着时间的推移可能会得到改进。这 并不能反映你作为一个程序员的能力

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

使用 BigDecimal,

或者使用 int 或者 long 进行货币计算,处理一下小数点。(数值不是太大的情况下)

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

基本类型只有值,装箱基本类型则具有与他们的值不同的同一性(值相等对象不等)

基本类型只有功能完备的值,装箱类型有非功能值:null

基本类型通常比装箱基本类型更节省时间和空间。

什么时候应该使用装箱基本类型? 当作为集合中的元素、键和值时必须使用装箱基本类型。

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

  • 字符串不适合代替其他的值类型
  • 字符串不适合代替枚举类型
  • 字符串不适合代替聚合类型。如 String compoundKey = className + "#" + i.next()
  • 字符串不适合代替 capabilities

§ 当心字符串连接的性能

对于生成单行输出或构造一个小的、固定大小的对象的字符串表示形式,它是可以的,但是它不能伸缩。

使用字符串串联运算符重复串联 n 个字符串需要 n 的 平方级时间。 这是字符串不可变这一事实导致的结果。当连接两个字符串时,将复制这两个字符串的内容。

使用 StringBuilderappend() 方法。

§ 通过接口引用对象

应该使用接口而不是用类作为参数的类型。如果有合适的接口类型存在,对于参数、返回值、变量和域来说,就都应该使用接口类型进行声明。

只有当你利用构造器创建某个对象的时候,才真正需要引用这个对象的类。

应当是:

1
Set<Son> sonSet = new LinkedHashSet<>();

而不是:

1
LinkedHashSet<Son> sonSet = new LinkedHashSet<>();

这样有一些好处:

  • 程序更加灵活,当要换实现时,只要改变构造器中类的名称。
  • 新的实现要提供相同的功能(通过接口来进行约束)。

§ 接口优先于反射机制

反射需要付出一些代价:

  • 丧失编译时类型检查的好处。
  • 执行反射访问的代码笨拙冗长。
  • 性能损失

核心反射机制为了基于组件的应用创建工具而设计的。这类工具需要根据需要装载类,反射找出他们支持哪些方法和构造器。允许用户交互式构建出访问这些类的应用程序。

复杂的应用程序可能使用反射,包括类浏览器、对象监视器、代码分析工具。

§ 谨慎的使用本地方法

JNI 允许 Java应用程序可以调用本地方法。为了提高性能,很少建议使用本地方法。

现在使用本地方法的目的,很多是为了做 软件加密与签名

§ 谨慎的进行优化

优化弊大于利,特别是不成熟的优化。

不要因为性能而牺牲合理的接口。努力编写 好的程序而不是快的程序

努力避免那些限制性能的设计决策。

§ 遵循普遍接受的命名惯例