为什么javadoc的Double.value说它缓存值,而实际上并非如此?



在OpenJDK中,对于方法:

public static Double valueOf(double d)

javadoc说:

返回一个Double实例,表示指定的Double值。如果不需要新的Double实例,则通常应该优先使用此方法,而不是构造函数Double(Double),因为该方法通过缓存频繁请求的值可能会产生明显更好的空间和时间性能。

下面是实际代码:
public static Double valueOf(double d) {
    return new Double(d);
}

缓存是一个谎言!这是怎么回事?

该方法适用于多种类型:Integer, Long, BigDecimal和其他类型,并且文档总是相同的:在某些情况下(未定义),方法可以返回相同的结果。

AFAIK,缓存只对整数类型实现,它返回值在-128到127(最常见的值)之间的缓存实例。对于BigDecimal,缓存当前适用于从0到10的值。

Java 的后续版本可能会将此行为扩展到其他值/更多类型。所以今天使用这段代码是明智的,因为它可能会让你明天的代码更快(今天的代码不会变慢)。

例如,Java编译器在生成自动装箱代码时使用此API。

API文档没有问题:

这个方法很可能产生…

也就是说,允许实现在这里进行缓存,这在构造函数中是不可能的。然而,这并不是必需的。但是,由于您有可能有执行缓存的实现,因此应该优先使用此方法,而不是使用构造函数。

从Java 1.5+开始,JVM/JIT保证Integer s -127到127的缓存。这就是为什么对于Integer,首选的方法是使用valueOf。您通常应该使用valueOf而不是double的构造函数,因为这样JIT就能够优化您的代码,因为它认为合适。例如,考虑以下循环:

for (Object o: objectList) {
  o.setValue(Double.valueOf(0.0));
}

在本例中,JIT可以预先计算double对象,并在每次循环迭代时重新赋值相同的值,而如果使用new Double(0.0);,则无法做到这一点。

API的设计者可能不希望限制替代实现。现在可以自由地将缓存添加到Double类中。

这些valueOf()方法存在于每个数字类型中,目的是为了支持缓存。事实上,对于Double,它不使用任何缓存,但对于IntegerLong

请记住,创建JVM是为了减少嵌入式设备(主要是)的代码大小——它是一个机顶盒操作系统。我在一些嵌入式java平台上工作过,在这些平台上,valueOf的"值"会更明显,在某些情况下,它会节省相当多的空间。

大多数情况下,这个方法的存在是因为"new"不能使用缓存实例。valueOf可以被实现为使用缓存的实例(否则你只会一直使用new),并且很可能在证明节省时间的地方这样做。

如果他们(或你)用一个真正缓存值的方法代替了那个方法,那么你所有的代码都将获得这种改变的好处,但如果没有准备提供一个像"valueOf"这样的方法,它就永远不会发生(好吧,实际上永远不会——你可以调整编译器/字节码执行器让"new"返回缓存值,但我认为这会破坏一些契约)

所以缓存并不是真正的谎言,只是一种心态。

最新更新