Effective Java 第三版读书笔记——条款6:避免创建不必要的对象

通常来讲,重用一个对象比创建一个功能相同的对象更加合适。重用速度更快,并且更接近现代的代码风格。如果对象是不可变的(条款 17),它总是可以被重用。

考虑一个极端的例子:

String s = new String("bikini"); // DON'T DO THIS!

这个语句每次执行时都会创建一个新的 String 实例,而这些实例的创建都是不必要的。如果这种用法发生在循环或者频繁调用的方法中,就会创建数百万个毫无必要的 String 实例。

改进后的版本如下:

String s = "bikini";

该版本使用单个 String 实例,而不是每次执行时创建一个新实例。

一些对象的创建可能会比其他对象的创建昂贵很多。 如果要重复使用这样一个“昂贵的对象”,建议将其缓存起来以便重用。 不幸的是,创建这样一个对象并不总是很直观的。 假设你想写一个方法来确定一个字符串是否是一个合法的罗马数字。 下面是使用正则表达式完成此操作的最简单方法:

// Performance can be greatly improved!
static boolean isRomanNumeral(String s) {
    return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
            + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}

这个实现的问题在于它依赖于 String.matches 方法。 虽然 String.matches 是检查字符串是否与正则表达式匹配的最简单方法,但它不适合在性能临界的情况下重复使用。 因为它在内部为正则表达式创建一个 Pattern 实例,并且只使用一次,之后这个 Pattern 实例就会被 JVM 进行垃圾回收。 创建 Pattern 实例是昂贵的,因为它需要将正则表达式编译成有限状态机(finite state machine)。

为了提高性能,将正则表达式显式编译为一个 Pattern 实例(不可变)并且缓存它,在 isRomanNumeral 方法的每个调用中重复使用相同的实例:

// Reusing expensive object for improved performance
public class RomanNumerals {
    private static final Pattern ROMAN = Pattern.compile(
            "^(?=.)M*(C[MD]|D?C{0,3})"
            + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");

    static boolean isRomanNumeral(String s) {
        return ROMAN.matcher(s).matches();
    }
}

如果经常调用,改进之后的 isRomanNumeral 会使性能得到显著提升。 而且将不可见的 Pattern 实例显式创建允许我们给它起一个名字,这个名字通常比正则表达式本身更具可读性。

另一种创建不必要的对象的方式是自动装箱(autoboxing),它允许程序员混用基本类型和包装的基本类型,根据需要自动装箱和拆箱。 自动装箱模糊不清,但不会消除基本类型和装箱基本类型之间的区别。 考虑下面这个计算 int 范围内正整数总和的方法。 要做到这一点,程序必须使用 long 类型,因为 int 类型不足以保存最后的结果:

// Hideously slow! Can you spot the object creation?
private static long sum() {
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
        sum += i;
    return sum;
}

这个程序的结果是正确的,但由于写错了一个字符,运行的结果要比实际慢很多。变量 sum 被声明成了 Long 而不是 long ,这意味着程序构造了大约 \(2^{31}\) 个不必要的Long实例(每次往 Long 类型的 sum 变量中增加一个 long 类型的 i)。把 sum 变量的类型由 Long 改为 long 会使性能得到很大提升。这个教训很明显:优先使用基本类型而不是包装的基本类型,也要注意无意识的自动装箱

这个条目不应该被误解为暗示对象创建是昂贵的,应该避免创建对象。 相反,创建和回收小的对象非常廉价,构造器只会做很少的工作,尤其在现代 JVM 实现上。 创建额外的对象以增强程序的清晰性,简单性或功能性通常是件好事。

posted @ 2018-11-10 16:22  LeeFire  阅读(175)  评论(0编辑  收藏  举报