23

我对 Java 处理数字的方式==以及equals()何时处理int,Integer和其他类型的数字有点困惑。例如:

Integer X = 9000;
int x = 9000;
Short Y = 9000;
short y = 9000;
List<Boolean> results = new ArrayList<Boolean>();
// results.add(X == Y); DOES NOT COMPILE        1)
results.add(Y == 9000);                      // 2)
results.add(X == y);                         // 3)
results.add(X.equals(x));                    // 4)
results.add(X.equals(Y));                    // 5)
results.add(X.equals(y));                    // 6)
System.out.println(results);

输出(也许你应该先做出猜测):

[true, true, true, false, false]
  1. X == Y不编译是可以预料的,是不同的对象 。
  2. 我有点惊讶,因为 9 默认情况下是 an Y == 9,并且 1) 甚至没有编译。请注意,您不能将 a放入期望 a 的方法中,但在这里它们是相等的。trueintintShort
  3. 出于与两个相同的原因,这令人惊讶,但似乎更糟。
  4. 毫不奇怪,x自动装箱到和Integer.
  5. 这并不奇怪,因为不同类中的对象不应该是equal().
  6. 什么?? X == ytrue但是X.equals(y)false?不应该==总是比 更严格equals()吗?

如果有人能帮助我理解这一点,我将不胜感激。为什么 == 和 equals() 会这样?

编辑: 我已将 9 更改为 9000 以表明此行为与从 -128 到 127 的整数的任何异常行为方式无关。

第二次编辑 好的,如果你认为你理解这些东西,你应该考虑以下几点,以确保:

Integer X = 9000;
Integer Z = 9000;
short y = 9000;
List<Boolean> results = new ArrayList<Boolean>();
results.add(X == Z);                      // 1)
results.add(X == y);                      // 2)
results.add(X.equals(Z));                 // 3)
results.add(X.equals(y));                 // 4)
System.out.println(results);

输出:

[false, true, true, false]

原因,据我所知:

  1. 不同的例子,如此不同。
  2. X未装箱,然后是相同的值,所以相等。
  3. 相同的价值,如此平等。
  4. y不能装箱,Integer所以不能相等。
4

8 回答 8

22

(小)整数实例被缓存,因此不变量 x == y 适用于小实例(实际上是 -127 +128,取决于 JVM):

Integer a = 10;
Integer b = 10;

assert(a == b); // ok, same instance reused

a = 1024;
b = 1024;

assert(a == b); // fail, not the same instance....
assert(a.equals(b)); // but same _value_

编辑

4) 和 5) 产生错误,因为equals检查类型:X是 Integer 而是YShort。这是java.lang.Integer#equals方法:

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }

    return false;
}
于 2009-08-11T11:13:05.560 回答
13

的原因

X == y

真实与二进制数字提升有关。当相等运算符的至少一个操作数可转换为数值类型时,使用数值相等运算符。首先,第一个操作数被拆箱。然后,两个操作数都转换为int.

尽管

X.equals(y)

是一个普通的函数调用。如前所述,y将自动装箱到Short对象。Integer.equals如果参数不是Integer实例,则始终返回 false。通过检查实现可以很容易地看到这一点。

有人可能会说这是一个设计缺陷。

于 2009-08-11T11:26:41.463 回答
7

故事的精神:

自动装箱/拆箱令人困惑,类型提升也是如此。它们共同构成了一个很好的谜语,但也是可怕的代码。

在实践中,使用小于 int 的数字类型很少有意义,而且我几乎倾向于配置我的 eclipse 编译器以将所有自动装箱和 -unboxing 标记为错误。

于 2009-08-11T11:34:00.543 回答
3

您的问题不仅在于它如何处理 ==,还在于自动装箱...当您比较 Y 和 9 时,您正在比较两个相等的基元,在最后两种情况下,您会得到错误,因为这就是 equals 的工作方式。两个对象只有当它们属于同一类型并且具有相同的值时才相等。当您在“X.equals(y)”中说时,您是在告诉它执行 Integer.equals(Short) 并查看 Integer.equals() 的实现,它将失败:

   public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
    }

由于自动装箱,最后两个将导致相同的失败,因为它们都将作为 Shorts 传入。

编辑:忘了一件事......在 results.add(X == y); 的情况下 它将取消装箱 X 并执行 (X.intValue() == y) 这恰好是真的以及 9 == 9

于 2009-08-11T11:18:46.237 回答
1

关于自动装箱如何工作以及如何缓存“小”值 Integer 对象的更多细节:

当原始 int 被自动装箱为 Integer 时,编译器通过调用 Integer.valueOf(...) 替换代码来实现这一点。因此,以下内容:

Integer a = 10;

由编译器替换为以下内容:

Integer a = Integer.valueOf(10);

Integer 类的 valueOf(...) 方法维护一个缓存,该缓存包含 -127 到 128 之间所有值的 Integer 对象。如果您使用此范围内的值调用 valueOf(...),该方法将返回一个 pre-缓存中的现有对象。如果值超出范围,则返回一个用指定值初始化的新 Integer 对象。(如果你想确切地知道它是如何工作的,在你的 JDK 安装目录中查找文件src.zip,并在其中查找类 java.lang.Integer 的源代码。)

现在,如果你这样做:

Integer a = 10;
Integer b = 10;
System.out.println(a == b);

你会看到true被打印出来——但不是因为 a 和 b 具有相同的值,而是因为 a 和 b 引用同一个 Integer 对象,即 Integer.valueOf(...) 返回的缓存中的对象。

如果更改值:

Integer a = 200;
Integer b = 200;
System.out.println(a == b);

然后打印false,因为 200 超出了缓存的范围,因此 a 和 b 指的是两个不同的 Integer 对象。

不幸的是,== 用于值类型的对象相等,例如 Java 中的包装类和 String - 这是违反直觉的。

于 2009-08-11T14:22:16.523 回答
1

如果需要,Java 会自动将 Integer 转换为 int。同样适用于短。此功能称为自动装箱和自动拆箱。你可以在这里阅读。

这意味着当您运行代码时:

int a = 5;
Integer b = a;
System.out.println(a == b);

Java将其转换为:

int a = 5;
Integer b = new Integer(a);
System.out.println(a == b.valueOf());
于 2009-08-11T11:14:34.590 回答
1

这种自动转换称为自动装箱。

于 2009-08-11T11:17:07.567 回答
1

我记得覆盖 "equal(object obj)" 的一个好习惯是首先检查传入参数的类型。所以也许这会导致X.equals(Y)false。您可以检查源代码以找出真相:)

于 2009-08-11T11:23:00.610 回答