What a variable really holds¶
By the end of this page you can say what an object variable actually contains, predict what happens when two variables refer to the same object, and explain why comparing two objects with == almost never does what you want.
This is the session that decides whether the rest of the semester feels logical or feels like magic. Almost every confusing bug a first-year Java programmer hits comes from believing a variable holds an object. It does not, and once you can see the real picture, a whole family of problems stops being mysterious.
What new actually did¶
Last session you wrote this and moved on:
Three separate things happened.
Java set aside space somewhere for an account and filled it in. Then it produced a reference, which is the way to reach that space. Then it stored that reference in ada.
The account and the variable are not the same thing and are not in the same place.
This picture is the one to keep. Every module from here zooms into some part of it. When inheritance arrives in week 7, it is this picture with the object drawn in two layers. When you save data to a file in week 11, it is this picture with an arrow leaving the program.
Primitives are different. A double variable really does hold the number. int, double, boolean, char and the rest hold their value directly. Everything else in Java holds a reference.
Two names, one account¶
Here is where it bites.
Account ada = new Account("AC-1001", "Ada Lovelace", 500.00);
Account backup = ada;
backup.withdraw(200.00);
System.out.println(ada.getBalance()); // 300.00
Nothing was copied. backup = ada copied the arrow, so now there are two arrows and still one account. Withdraw through either name and the other one sees it, because there is nothing else to see.
Account ada = new Account("AC-1001", "Ada Lovelace", 500.00);
Account alan = new Account("AC-1001", "Ada Lovelace", 500.00);
These two lines make two accounts. Identical values, separate things. Withdraw from one and the other is untouched.
== compares arrows¶
Given the picture, this stops being surprising.
Account ada = new Account("AC-1001", "Ada Lovelace", 500.00);
Account alan = new Account("AC-1001", "Ada Lovelace", 500.00);
System.out.println(ada == alan); // false
== asks whether the two arrows point at the same place. They do not. It never looked at the values inside, and it never will, no matter how many fields match.
So == answers a real question: are these the same object? That is sometimes exactly what you want. It is almost never what you want when you are asking whether two accounts are the same account.
The proper answer is a method called equals, which compares what you decide matters. Writing it correctly has rules, and we do it in week 5 where it belongs. For now, know that == on objects means identity, not sameness.
This applies to String too, and it will catch you
String is an object, so == on two strings compares arrows.
String a = new String("AC-1001");
String b = new String("AC-1001");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
Confusingly, String a = "AC-1001"; written twice often does give true, because Java reuses identical literals behind the scenes. So == on strings appears to work, right up until the string came from a file or from user input, and then it silently stops working.
Use .equals() for strings. Always. There is no case in this course where == on strings is the right answer.
An arrow pointing nowhere¶
A reference variable can hold no arrow at all.
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "Account.getBalance()" because "closed" is null
null is not an empty account. It is the absence of one. Asking it for a balance is following an arrow that does not exist, and the program stops.
Modern Java tells you exactly which variable was null, which is a large improvement over what your predecessors got. Read that message. It names the variable and the method, and between those two facts the cause is usually obvious.
Where does null come from? Usually from a field nobody set, or from a method that searched for something and did not find it. Week 4 gives you the second case for real, when findAccount has to answer "no such account".
Handing an object to a method¶
Now the part that looks like an exception to the rules and is not.
Account ada = new Account("AC-1001", "Ada Lovelace", 500.00);
applyFee(ada);
System.out.println(ada.getBalance()); // 495.00
The change survived, which surprises people who were told Java always copies.
Java did copy. It copied the arrow. The method got its own variable called account, holding a second arrow to the same object, exactly like backup earlier. Two arrows, one account, so the fee lands on the real thing.
There is a real limit to this, and it is worth seeing once.
Call that with ada and ada is unchanged. The method pointed its own arrow somewhere else. The caller's arrow never moved. You can change the object a method was handed. You cannot change which object the caller is looking at.
If you remember one thing. A variable never holds an object. It holds the way to reach one, and several variables can hold the way to reach the same one.
Check yourself¶
Predict first, then run
Write this in main. Before you run it, write down on paper what each of the four lines will print.
Account ada = new Account("AC-1001", "Ada Lovelace", 500.00);
Account backup = ada;
Account twin = new Account("AC-1001", "Ada Lovelace", 500.00);
backup.withdraw(200.00);
System.out.println(ada.getBalance()); // 1
System.out.println(twin.getBalance()); // 2
System.out.println(ada == backup); // 3
System.out.println(ada == twin); // 4
Now run it. For every line you got wrong, do not just accept the answer. Draw the boxes and arrows on paper until the picture explains the output, then check your drawing against the first diagram on this page.
Then one more, which is the one that matters:
Account nothing = null;
System.out.println(nothing == null); // fine
System.out.println(nothing.getBalance()); // not fine
Run it and read the exception message properly, all of it. You will meet this message more often than any other in Java, and being able to read it is a skill worth ten minutes now.
Think about it¶
-
backup = adacopies an arrow rather than the account. Suppose you genuinely wanted a separate account with the same values. You cannot get it from an assignment. What would you have to do instead, and what would you have to decide along the way? -
==compares arrows, which means two accounts with the same number are not equal. For a bank, is that the right default or the wrong one? Argue it, then consider a system where the same customer record is loaded twice from a file. -
A method can change the object you handed it but cannot change which object you are looking at. Is that a sensible rule or an accident of how Java works? Think about what the alternative would let a method do to your variables.
-
Nobody points an arrow at the object made inside
replaceonce the method ends. What happens to it? You have not been taught the answer. Work out what must happen, and what would go wrong if it did not.