Let's consider the following code:
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);
I'm creating a static field belonging to the Testing class and this field is shared among the two instances of that class t1
and t2
as well. Then I test if they reference to the same value in memory and indeed, they do, the result is true. And that's clear for me.
However, if I delete the static keyword from the declaration of int i
, something unexpected happen.
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);
I would expect the two instances t1
and t2
to both have 47 as a value of their field but their fields be in different memory addresses. But surprisingly, when test for t1.i == t2.i
I get true as well in this case - why? The field int i = 47;
is not static anymore so I would expect it to be in different memory addresses for every instance of the class but the equality yields true.
An int
is a primitive type, not a reference type. The condition t1.i == t2.i
does not test for reference equality -- there is no reference in the first place here. It simply compares the values and in this case, both have the value 47
.
If you had a member field that is not a primitive, the result would be different, for example:
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
}
}
In this case, each instance has a different reference to an Integer
object created using the new
keyword which invokes a constructor, and the condition t1.i == t2.i
compares these two references.
There is already good answers on why your ==
test just works on native int
even when you create 2 instances.
I just want to point some weird stuff the compiler can do behind the scene that can give counterintuitive results. Consider the following test:
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);
}
}
You expect
either true, true, true, true
because compiler is smart at identifying that, even there are 2 instances of Foo
, they have the same value for i
and j
.
or true, false, true, false
, because, after all, i
and j
are different instances of Integer
, and ==
compares the reference values that should not be the same.
But the surprise is you actually get true, true, true, false
. What the heck is the difference between i
and j
?
Well, if you look at the generated code with javap -verbose Foo.class
, you see:
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
The compiler has generated code that use Integer#valueOf(int). And the doc states:
This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
This explains why the 2 instances of Foo
actually share the same Integer
object for 47
, but not for 1234
.
The moral is: beware of ==
when you are comparing objects. In most cases, what you want and need is equals()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With