☰ Contents How Java Runs

When Some Values are More Equal than Others

Student: Professor, my Java is broken.
Prof: What makes you say that?
Student: Well, the same thing sometimes returns true and sometimes returns false:
Prof: Fascinating! That’s a cool example you came up with to test. And I think you’re frustrated because the behavior is different than for numbers?
Student: Right. If I do 6 + 7 == 13 that’s clearly true. So why isn’t trash + " panda" == "trash panda"? And I don’t think it’s fascinating I think it’s annoying! The same thing happens with arrays, look:
Prof: Indeed. Alright, it’s time to unpack one of the things I told you to wait on. We need to talk about reference types vs primitive types and also introduce the idea of different parts of memory.
Student: Yes, please, make it make sense!
Prof: I often find it’s best to describe memory with a picture. I’ll focus on the array picture program. Here’s how I like to draw it:
Names (the stack)
top level
nums1 = @1001
nums2 = @1002
nums3 = @1002
same1and2 = false
same1and3 = false
same2and3 = true
Heap
@1001: [6, 7]
@1002: [6, 7]

Hover over an address like @1001 to draw an arrow to its object on the heap; click or tap it to keep the arrow showing.

Prof: OK, so stepping way back – our computers store data in memory. You can think of memory in Java as being split into two parts:

  • The stack which holds all the definitions at the top level, all method arguments, and all definitions of variables inside methods
  • The heap which holds all created objects. The objects we’ve seen so far are record instances like Point[...] or Student[...], arrays, and as it turns out, Strings. When an object is created, like with new or with {"array", "initialization"}, Java makes space for it on the heap and returns a reference to it. Only the reference actually gets stored in variables, fields, or array elements.

This picture makes that explicit. See the things that look like @1001 and @1002? Those are the references. So we can see that the value stored in nums2 and the value stored in nums3 are the same reference. In contrast, nums1 holds a separate reference to a different array with the same contents.

The last piece we need to understand this is that == compares references, not contents, of objects. So same1and2 is calculating @1001 == @1002, which is false, while same2and3 is calculating @1002 == @1002, which is true.

Whew, that was a lot. What do you think?

Student: It’s helping a little bit. Can we see what’s going on with the first example?
Prof: It looks like this in memory:
Names (the stack)
top level
tp1 = @1001
trash = @1002
tp2 = @1003
tp3 = @1003
same1and2 = false
same1and3 = false
same2and3 = true
Heap
@1001: "trash panda"
@1002: "trash"
@1003: "trash panda"
Prof: Same shape, more or less, but the heap contents are the characters in the strings as opposed to arrays. Just like arrays, using == on strings compares the references, not the contents, which explains the output.
Student: OK, this seems really complicated, but I guess it does explain what I was seeing.

Practice

Java hands out references in the order objects are created, starting at @1001. Fill in the memory picture for this code:

int[] a = { 3, 4 };
int[] b = { 3, 4 };
int[] c = b;
int[] d = a;
boolean ab = a == b;
boolean bc = b == c;
boolean ad = a == d;
Names
a =
b =
c =
d =
ab =
bc =
ad =

Heap
@1001: [3, 4]
@1002: [3, 4]

Same rules, starting again at @1001. This time one of the objects holds references of its own:

String word = "hi";
String loud = word + word;
String same = loud;
String[] pair = { word, loud };
boolean match = pair[1] == same;
Names
word =
loud =
same =
pair =
match =

Heap
@1001: "hi"
@1002: "hihi"
@1003: [, ]