Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are non-static fields static until they're changed in Java?

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.

like image 292
Vincent Avatar asked Dec 20 '22 05:12

Vincent


2 Answers

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.

like image 178
M A Avatar answered Dec 21 '22 17:12

M A


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().

like image 45
T.Gounelle Avatar answered Dec 21 '22 19:12

T.Gounelle