为什么 (i<=j && j<=i && i!=j) 的结果是TRUE?

106

我编写了一段Java代码,它在一个无限循环中运行。

以下是代码:

public class TestProgram {
    public static void main(String[] args){
        Integer i = new Integer(0);
        Integer j = new Integer(0);

        while(i<=j && j<=i && i!=j){
            System.out.println(i);
        }
    }
}
在上面的代码中,在while循环中看到条件时,起初似乎程序不会进入while循环。但实际上它是一个无限循环并且不断地打印值。
这里发生了什么?

8
简单的回答是 i<=j && j<=i && i!=j 这个条件总是为真。拿一张纸来计算一下,你就能明白了 :) - Pradeep Simha
4
你创建整数的方法不正确。请使用“compareTo”。 - nachokk
7
如果您从未更改 ij,您期望循环何时终止? - Fred Larson
33
对于简单的整数值,这将始终产生 false。从 i<=jj<=i 可以得出结论,即 i == j,这与最后一个术语相矛盾。因此,整个表达式计算为 false,并且 while 循环不会执行。关键点在于对象标识! - Sirko
4
值得一提的是,这是《Java谜题:陷阱、缺陷和边角案例》一书中的第32个谜题。 - Cyanfish
显示剩余5条评论
10个回答

189
  • i <= j被评估为true,因为int比较自动拆箱发生,然后ij都保存默认值0.

  • j <= i由于上述原因被评估为true

  • i != j被评估为true,因为ij是不同的对象。在比较对象时,没有任何需要进行自动拆箱的需要。

所有条件均为真,并且您没有在循环中更改ij,因此它会无限运行。


10
可以请你解释一下为什么"!="检查引用对象的内存索引,而"<="检查Integer对象的非装箱值?为什么这些运算符之间会有这样的差异? - Punith Raj
41
@PunithRaj < & > 运算符作用于基本数据类型而不是对象,因此在使用这些运算符时会自动进行拆箱操作。但是 == 和 != 运算符也可用于对象比较,因此在这里不需要拆箱,因此对比的是对象。 - Juned Ahsan
14
啊,隐含装箱/拆箱的潜在危险啊! - Hot Licks
3
Stack Overflow应该添加一个新标签:“自动拆箱是Java中曾经犯下的最大错误”。:-)。除了《Java Puzzler》书籍的作者之外。使用它来标记这样的问题。 - user949300
4
请注意,Integer.valueOf(0) == Integer.valueOf(0) 的结果始终为true,因为在这种情况下返回的是相同的对象(请参见 IntegerCache http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Integer.java#Integer.IntegerCache)。 - Vitalii Fedorenko
显示剩余9条评论

41

因为你正在进行比较操作

  • 0 < = 0 (true) // 拆箱

  • 0 > = 0 (true) // 拆箱

  • reference != secondReference (true) 因为你正在创建对象,而不是原始类型比较。所以它的结果是while(true) { // 永远不会结束的循环 }


2
哦!自动拆箱的隐藏龙...好的解释。 - HybrisHelp

17

无论真假,在这里都没有关系,也不回答问题。 - Kon
6
实际上这就是答案。条件1和条件2由于自动装箱而被评估为“true”。在第3种情况下,不适用自动装箱,因此比较是在对象(内存位置)级别上进行的。 - home

2

首先,我们需要了解两种不同的情况:

情况1:

        Integer i = new Integer(10);
        Integer j = new Integer(10);

        System.out.println((i<=j && j<=i && i!=j));
        System.out.println(i!=j);

第二种情况:

        Integer i = 10;
        Integer j = 10;

        System.out.println((i<=j && j<=i && i==j));
        System.out.println(i==j);

两种情况是不同的,因为

在第一种情况下:i!=j将会是true,因为它们都引用堆中的两个不同对象,不能相同。但是

在第二种情况下:i==j将会是true,因为两个10都是整数字面量,Java维护了整数字面量池,其值为(-128 <= X <= 127)。所以,在这种情况下,10<=127结果为true,因此两者将引用同一个对象。


1
整数对象是不同的。它与基本的int类型不同。所以你可以这样做。你所做的就是比较对象,当然结果是true。

1

Integer a = new Integer(0); Integer b = new Integer(0);

<= 和 >= 比较将使用未装箱的值 0,而 != 将比较引用并成功,因为它们是不同的对象。

即使这也可以工作:

Integer a = 1000; Integer b = 1000;

但这却不行:

Integer a = 100; Integer b = 100;

原因是 Integer 在内部使用缓存来缓存 -128 到 127 之间的 Integer 对象,并为其覆盖的范围返回实例。我不确定,但我猜您还可以在包“java.lang.Integer.IntegerCache.high”中更改其最大值。

为了更好地理解,请查看网址:https://www.owasp.org/index.php/Java_gotchas#Immutable_Objects_.2F_Wrapper_Class_Caching


1
循环不会结束,因为条件是真的(i != j 是真的,因为有两个不同的对象,使用Integer.valueOf代替),并且在循环内部值没有改变,所以条件永远保持为真。

1
也许原因在于 'i' 和 'j' 都是对象,对象的比较不同于对象引用的比较。请考虑使用 !i.equals(j) 替代 i!=j。

0
程序一直显示相同的i值,因为您没有增加或减少ij的值。for循环中的条件始终保持为真,因此这是一个无限循环。

我认为问题更多地涉及到i!=j部分,这部分令人惊讶地评估为真,而不是<=比较。 - Soravux

-3

你必须知道,在编程中,使用 && 和 & 时有一些不同。当你使用 && 时,如果第一个条件为真,则会检查第二个条件,如果第二个条件为假,则不会检查第三个条件,因为在 & 运算符中,如果一个条件为假,则所有语句都为假。如果使用 || 运算符,则如果它看到 true,则返回 true。在你的代码中,因为 i 和 j 相等,所以第一个和第二个条件都为真,但是在第三个条件中,它将为假,因为它们相等,而 while 条件为假。


我不知道为什么我的答案会得到“mines”值,因为我的答案是正确的。请查看此链接,它是正确的。在获取“mines”之前,请阅读更多信息:https://dev59.com/OG035IYBdhLWcg3wMNGW - sara Sodagari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接