为什么我们在java中更喜欢原语和盒装原语

时间:2016-08-17 18:55:07

标签: java

我正在阅读有效的java第二版,第23页说

// Hideously slow program! Can you spot the object creation
public static void main(String[] args) {
    Long sum = 0L;
    for(long i=0; i<=Integer.MAX_VALUE; i++){
        sum += i;
    }
    System.out.println(sum)
}

作者说上面的代码不必要地生成了2 ^ 31个对象实例。为什么sum + = i生成新的Object?如果我将声明改为

sum = sum + 1

没有这种副作用?

6 个答案:

答案 0 :(得分:4)

由于autoboxing因为您的变量sum不是基本类型而是类型Long(包装类),sum += i将在场景后面创建一个新{ {1}}实例,因为它是一个不可变的类,因此它将以某种方式等同于Long

答案 1 :(得分:4)

尝试以更清晰的方式重新阐述其他人所说的内容:

sum的问题是 L ong是引用类型;换句话说,它是某种对象。对象生活在堆上;它们是由JVM创建的(使用&#34; new&#34;和构造函数),并且#34;托管&#34;由垃圾收集器。

自动装箱功能允许您使用该引用类型的Long变量,就像使用基本类型的长变量一样。

但Long对象是不可变的;一旦创建,它的价值永远不会改变。但整个循环是关于不断改变一个值(通过递增计数器)!因此,要增加计数器,您必须获取&#34;当前&#34;的值。长物;加1;并将其填入下一个Long对象。再次,再次,......

所以,你的程序在这里做的是:始终创建垃圾。 换句话说:创建那些Long对象;使用一次(检索它们的价值);然后他们被遗忘了#34; (因为在任何地方都没有提及它们)。因此,他们立即有资格获得垃圾回收

含义:这里实际上对性能有两个影响:

  1. 不必要的对象创建[在Java中相当便宜,但仍然#34;更昂贵&#34;而不是对长值的简单计算;后者可能只是一个,两个CPU指令;而对象创建将导致内存访问和相当一些CPU操作! ]
  2. 高速创建需要进行垃圾回收的对象。

答案 2 :(得分:3)

sum = sum + 1仍然会遇到与sum += 1相同的问题,因为右侧的sum会将值取消装箱,然后向其添加1,最后在其中创建一个新的Long对象命令将结果打包并指定sum引用变量以指向新创建的对象。

答案 3 :(得分:3)

这是因为java.lang.Long类是不可变的。换句话说,在循环的每次迭代中创建一个新的Long,而不是在原位上改变原始。与原始long相比,盒装Long带来了很多包袱。您的替代语句sum = sum + 1将与原始代码具有相同的效果。将sum重新标记为long sum = 0L;会在每次迭代时创建一个新的long,其创建的基本Long更便宜。

答案 4 :(得分:3)

由于$sum数据类型,它是java中的不可变类。 在语句Long中,您试图改变(更改)sum += i;的值.java编译器在循环的每次迭代期间自动创建一个新的sum对象,然后再分配它创建了对Long的引用。

  

因此,该书的作者说sum

答案 5 :(得分:2)

因为每次总结时,都会创建一个新的Long对象。

Long's是不可变的,因此您无法更改该值,因此在每次迭代时都会使用总和创建一个新值。如果它是原始的,那么它将修改其当前值。