让我们考虑以下代码:
public class Testing {
static int i = 47;
public static void main(String[] args) {
Testing t1 = new Testing();
Testing t2 = new Testing();
System.out.println(t1.i == t2.i);
我正在创建一个属于Testing类的静态字段,该字段也在该类t1
和t2
的两个实例之间共享。然后我测试它们是否在内存中引用相同的值,实际上,它们确实如此,结果是真的。这对我来说很清楚。
但是,如果我从int i
的声明中删除static关键字,则会发生意外情况。
public class Testing {
int i = 47;
public static void main(String[] args) {
Testing t1 = new Testing();
Testing t2 = new Testing();
System.out.println(t1.i == t2.i);
我希望两个实例t1
和t2
都有47作为其字段的值,但它们的字段位于不同的内存地址中。但令人惊讶的是,在t1.i == t2.i
的测试中,我在这种情况下也是如此 - 为什么?字段int i = 47;
不再是静态的,所以我希望它对于类的每个实例都在不同的内存地址中,但是相等的结果为真。
答案 0 :(得分:6)
int
是基本类型,而不是引用类型。条件t1.i == t2.i
不测试引用相等性 - 这里首先没有引用。它只是比较值,在这种情况下,两者都具有值47
。
如果您的成员字段不是原始字段,则结果会有所不同,例如:
public class Testing {
Integer i = new Integer(47);
public static void main(String[] args) {
Testing t1 = new Testing();
Testing t2 = new Testing();
System.out.println(t1.i == t2.i); // false
}
}
在这种情况下,每个实例对使用调用构造函数的Integer
关键字创建的new
对象具有不同的引用,条件t1.i == t2.i
比较这两个引用。
答案 1 :(得分:2)
int的标识与对象的标识不同。
对象身份源自同一个对象, 原始类型标识是从identic值派生的(47 == 47)。
如果您将代码更改为Integer i = 47;
,则==
不会取消整理整数,但会比较对象引用,结果将为false。
如果如图所示初始化Integer(使用文字47),则自动装箱将从内部缓存中选择Integer对象。在身份比较(==)上,结果为真。如果使用Integer i = new Integer(47)`设置至少一个Integers,则比较将失败,因为您现在已经创建了一个新对象。如果超出缓存范围,使用带符号字节范围之外的文字初始化整数,则同样适用。然后运行时将为文字创建一个新的Integer,而identity compare将返回false。
我认为这是拆箱的一个不好的警告,如果你真的想要比较整数的引用而不是值,那么很难找到问题。
我使用以下代码来说明不同的行为。我现在认为简单地使用int literals实例化Integer
并依赖自动装箱是不好的习惯。这将对运行时行为产生微妙的变化。此外,如果您处理对象,请在保存侧并与.equals()
进行比较。
public class Test {
public static void main(String...args) {
Integer h = new Integer(47); // Object created w/o boxing
Integer i = new Integer(47);
Integer j = 47; // Object created with boxing
Integer k = 47; // due to caching, this is the same Integer
Integer j2 = 247; // Object created with boxing
Integer k2 = 247; // no caching, these are different Integers
int l = 47; // primitives
int m = 47;
// compare two explicit Objects
System.out.println((h == i) ? "true" : "false"); // false
// compare one explicit Object with a autoboxed Object
// compare is reference compare
System.out.println((h == j) ? "true" : "false"); // false
System.out.println((j == h) ? "true" : "false"); // false
// compare two autoboxed Objects, compare is by reference
// because value was in cache range, the Integers are identical
System.out.println((k == j) ? "true" : "false"); // true
// compare two autoboxed Objects, compare is by reference
// because value was not in cache range, these are two Objects of type Integer
System.out.println((k2 == j2) ? "true" : "false"); // false
// adding a primitive to the compare will
// always compare by value
System.out.println((i == l) ? "true" : "false"); // true
System.out.println((m == l) ? "true" : "false"); // true
}
}
更新:我将评论考虑在内并更新了我的示例,以包含intValue()
的意外缓存。
答案 2 :(得分:2)
即使您创建了2个实例,对于==
测试仅适用于原生int
的原因,已有很好的答案。
我只是想指出一些奇怪的东西,编译器可以在场景后面做些可以产生违反直觉的结果。考虑以下测试:
public class Foo {
final Integer i = 47;
final Integer j = 1234;
public static void main(String args[]) {
Foo p = new Foo();
Foo q = new Foo();
System.out.println(p.i.equals(q.i));
System.out.println(p.i == q.i);
System.out.println(p.j.equals(q.j));
System.out.println(p.j == q.j);
}
}
你期待
要么true, true, true, true
,因为编译器很聪明地识别出即使有Foo
的2个实例,它们也具有i
和j
的相同值。
或true, false, true, false
,因为i
和j
是Integer
的不同实例,==
比较参考值不应该是一样的。
但令人惊讶的是你真的得到了true, true, true, false
。 i
和j
之间的区别是什么?
好吧,如果你用javap -verbose Foo.class
查看生成的代码,你会看到:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #11 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 47
7: invokestatic #13 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
10: putfield #19 // Field i:Ljava/lang/Integer;
13: aload_0
14: sipush 1234
17: invokestatic #13 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
20: putfield #21 // Field j:Ljava/lang/Integer;
23: return
编译器生成了使用Integer#valueOf(int)的代码。该文件指出:
此方法始终缓存值的范围为-128到127(含),可以缓存此范围之外的其他值。
这解释了为什么Foo
的2个实例实际上共享Integer
的{{1}}个对象,而不是47
。{/ p>
道德是:在比较对象时要小心1234
。在大多数情况下,您想要和需要的是==
。
答案 3 :(得分:1)
static
所做的就是告诉解释器对象/变量仅包含在该类中,而无需在使用之前定义该类的实例(Object)。
发生这种情况的原因是因为这两个变量包含相同的值,因为它们都是值int
的{{1}},因此,比较它们会显示它们完全相同。
如果你想比较两个非原始类的相等,你可以使用它:
47
否则,如果你想比较两个对象/变量的类,你可以通过测试它来做到这一点:
if(t1.equals(t2)){
...
}
或者,如果您想检查它是否是另一个类的实例(扩展或继承它),您可以使用:
if(t1.getClass() == t2.getClass()){
...
}