2010-04-08 50 views
11

Autoboxing是相当可怕的。虽然我完全理解==.equals之间的区别我不能不帮助有后续错误的地狱了我:为什么编译器/ JVM不能让自动装箱“只是工作”?

final List<Integer> foo = Arrays.asList(1, 1000); 
    final List<Integer> bar = Arrays.asList(1, 1000); 
    System.out.println(foo.get(0) == bar.get(0)); 
    System.out.println(foo.get(1) == bar.get(1)); 

,打印

true 
false 

他们为什么这样做这种方式?这与缓存的整数有关,但如果是这种情况,为什么不缓存该程序使用的所有整数?或者为什么JVM不总是自动取消原始的框?

打印错误的假或真的真的会更好。

编辑

我不同意有关的旧代码破损。通过让foo.get(0) == bar.get(0)返回true,您已经破坏了代码。

不能在此可以在编译器级别由字节码与诠释更换整数(只要它永远不会赋值为null)

+8

它只是工作!不是你期望的方式;) – OscarRyz 2010-04-08 19:14:33

+0

实际上你的例子与自动装箱无关,它的行为早于它。确实,自动装箱迫使我们更加意识到它:equals()是比较对象的(通常是正确的)方式,==是比较原语的(唯一)方法... autoboxing帮助程序员处理Integer和int (几乎)可以互换......因此存在bug的危险。 – leonbloy 2010-04-08 19:18:22

+0

这可能是由于也影响String实例的大小写。就我所了解的情况而言,根据价值在幕后发生了一些联营活动。这可以通过使用'new'关键字来防止。 – 2010-04-08 19:33:23

回答

9
  • 他们为什么这样做这种方式?

-128和127之间的每个整数都被java高速缓存。据说,他们这样做是为了获得性能优势。即使他们现在想回到这个决定,他们也不可能。如果有人根据此代码构建代码,那么代码在被取出时会中断。对于业余爱好编码而言,这可能并不重要,但对于企业代码而言,人们会感到不安和发生诉讼。

  • 为什么他们不缓存程序使用的所有整数?

所有整数不能被缓存,因为内存的影响是巨大的。

  • 为什么JVM不总是自动的取消装箱到原始的?

因为JVM无法知道你想要什么。而且,这种改变很容易破坏那些不适合处理这种情况的遗留代码。

如果JVM在调用==时自动拆箱到原语,这个问题实际上会变得更加混乱。现在你需要记住==总是比较对象引用,除非对象可以被拆箱。这会导致更多奇怪的混乱情况,就像你上面所说的那样。

甚则担心太难了这一点,只记得这个规则,而:

NEVER比较==对象,除非你打算通过其引用来比较它们。如果你这样做,我想不出你遇到问题的场景。

+0

永不说**从不**。你**应该**比较'枚举值与==。 – 2010-04-08 21:21:44

+0

@Alexander Pogrebnyak - 你是对的,这就是为什么我添加了“除非你打算通过他们的引用比较它们”条款。这涵盖枚举。我站在我的**从来没有** :) – 2010-04-08 21:42:26

+2

公平点。然后你仍然有“杀死-9的许可证”:)。 007 out – 2010-04-08 21:51:44

7

你能想象的表现多么糟糕是,如果每一个Integer进行开销解决拘留? new Integer也不起作用。

Java语言(不是JVM问题)不能总是自动取消装箱,因为为1.5之前的Java设计的代码仍然可以工作。

+0

很好的提到1.5之前的代码。 +1 – Bozho 2010-04-08 19:49:26

+0

提供向后兼容性问题的另一个+1 – Chris 2010-04-08 19:52:01

+0

他们已经通过缓存破坏了旧代码-128到127 – Pyrolistical 2010-04-08 20:12:20

5

Integer s在字节范围内是相同的对象,因为它们被缓存。在字节范围外的Integer不是。如果所有整数都被缓存,则想象所需的内存。

而且从here

这一切神奇的结果是,你可以在很大程度上忽略int和整数之间的区别,有几个注意事项。整数表达式可以有一个空值。如果你的程序尝试autounbox null,它会抛出一个NullPointerException。 ==运算符对Integer表达式执行引用标识比较,并对int表达式进行值相等比较。最后,还有与装箱和拆箱相关的性能开销,即使是自动完成的

+0

'[-128,127]之外的整数'可能会或可能不会被执行。 (我认为原始问题理解发生了什么,但想知道为什么?) – 2010-04-08 19:36:15

+0

我认为他们不是与太阳的实现。无论如何,这是关于缓存它们和缓存所需的资源。 – Bozho 2010-04-08 19:47:30

4

如果您完全跳过自动装箱,仍然会出现此问题。

final List<Integer> foo = 
    Arrays.asList(new Integer(1), new Integer(1000)); 
final List<Integer> bar = 
    Arrays.asList(new Integer(1), new Integer(1000)); 

System.out.println(foo.get(0) == bar.get(0)); // false 
System.out.println(foo.get(1) == bar.get(1)); // false 

这是有原因的,为什么Eclipse已经自动装箱为默认警告:

final List<Integer> foo = 
    Arrays.asList(Integer.valueOf(1), Integer.valueOf(1000)); 
final List<Integer> bar = 
    Arrays.asList(Integer.valueOf(1), Integer.valueOf(1000)); 

System.out.println(foo.get(0) == bar.get(0)); // true 
System.out.println(foo.get(1) == bar.get(1)); // false 

如果你想有一个特定的行为更明确。

+0

它在Eclipse的副本中默认没有打开,我没有改变它。你使用什么版本?我检查了3.2和3.4。 – Chris 2010-04-08 19:57:10

+0

@Crhis:Eclipse Gallileo(3.5)Window-> Preferences Java-> Compiler-> Errors/Warnings-> Potential Programming Problems-> Boxing and Unboxing Conversions。也许它默认情况下不是开启的,但是自从我切换到Java 5以来,它就一直处于开启状态。 – 2010-04-08 21:15:33

+0

默认情况下它绝对不是。当我在这里阅读时,我只是改变了它。 – 2013-09-06 21:14:38

3

很多人都遇到过这个问题,即使是写Java书的人也是如此。

Pro Java Programming中,作者讲述了使用自动装箱整数作为IdentityHashMap中的一个键的问题,他在WeakHashMap中使用了自动装箱的Integer键。他使用的示例值大于128,因此他的垃圾收集调用成功。如果有人使用他的例子并使用小于128的值,他的例子将失败(由于密钥被永久缓存)。

2

当你写

foo.get(0) 

编译不要紧,你是如何创建的列表。它只查看List foo的编译时类型。所以,如果这是一个列表< Integer>,它会将它视为列表< Integer>,因为它应该这样做,而列表< Integer>的get()总是返回一个Integer。如果你想使用==那么你必须写

System.out.println(foo.get(0).intValue() == bar.get(0).intValue()); 

System.out.println(foo.get(0) == bar.get(0)); 

因为具有完全不同的意义。

相关问题