effective-Java/ch02创建和销毁对象/06.避免创建不必要的对象.md

48 lines
3.7 KiB
Markdown
Raw Normal View History

2019-09-17 20:17:31 +08:00
## 避免创建不必要的对象
### 示例
- [Item06Example01.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item06/Item06Example01.java):确定一个字符串是否是一个有效的罗马数字。
### 说明
在每次需要时重用一个对象而不是创建一个新的相同功能对象通常是恰当的。重用可以更快更流行。如果对象是不可变的(条目 17),它总是可以被重用。
通过使用静态工厂方法(static factory methods(项目1),可以避免创建不需要的对象。例如,工厂方法`Boolean.valueOf(String)` 比构造方法`Boolean(String`)更可取后者在Java 9中被弃用。构造方法每次调用时都必须创建一个新对象而工厂方法永远不需要这样做在实践中也不需要。除了重用不可变对象如果知道它们不会被修改还可以重用可变对象。
一些对象的创建比其他对象的创建要昂贵得多。 如果要重复使用这样一个“昂贵的对象”,建议将其缓存起来以便重复使用。
#### 缓存起对象以便重复使用
**示例**[Item06Example01.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item06/Item06Example01.java):确定一个字符串是否是一个有效的罗马数字
如果包含`isRomanNumeral`方法的改进版本的类被初始化,但该方法从未被调用,则`ROMAN`属性则没必要初始化。 在第一次调用`isRomanNumeral`方法时,可以通过延迟初始化( lazily initializing属性条目 83来排除初始化但一般不建议这样做。 延迟初始化常常会导致实现复杂化,而性能没有可衡量的改进(条目 67
#### 自动装箱
自动装箱autoboxing它允许程序员混用基本类型和包装的基本类型根据需要自动装箱和拆箱。
自动装箱模糊不清,但不会消除基本类型和装箱基本类型之间的区别。 有微妙的语义区别和不那么细微的性能差异(条目 61
**示例**[Item06Example02.java](CreatingAndDestroyingObjects/src/main/java/com/jueee/item06/Item06Example02.java):计算所有正整数的总和
```
2305843008139952128
UseTime1:10817
2305843008139952128
UseTime2:1405
```
`sumBad()` 由于写错了一个字符,运行的结果要比实际慢很多。变量`sum`被声明成了`Long`而不是`long`这意味着程序构造了大约231不必要的`Long`实例(大约每次往`Long`类型的 `sum`变量中增加一个`long`类型构造的实例)。
`sumGood()` 把`sum`变量的类型由`Long`改为`long`在我的机器上运行时间从10.8 秒降低到0.14秒。
这个教训很明显:**优先使用基本类型而不是装箱的基本类型,也要注意无意识的自动装箱**。
#### 维护自己的对象池
除非池中的对象非常重量级,否则通过维护自己的对象池来避免对象创建是一个坏主意。
对象池的典型例子就是数据库连接。建立连接的成本非常高因此重用这些对象是有意义的。但是一般来说维护自己的对象池会使代码混乱增加内存占用并损害性能。现代JVM实现具有高度优化的垃圾收集器它们在轻量级对象上轻松胜过此类对象池。
这个条目的对应点是针对条目 50的防御性复制defensive copying。 目前的条目说:“当你应该重用一个现有的对象时,不要创建一个新的对象”,而条目 50说“不要重复使用现有的对象当你应该创建一个新的对象时。”请注意重用防御性复制所要求的对象所付出的代价要远远大于不必要地创建重复的对象。 未能在需要的情况下防御性复制会导致潜在的错误和安全漏洞;而不必要地创建对象只会影响程序的风格和性能。