What belongs to whom¶
By the end of this page you can decide whether a value belongs to one object, to the class as a whole, or should never change at all, and you can write each of those in Java. Your accounts will also start giving themselves account numbers.
Two questions run through this session, and they are the same question asked twice. Where does this value live? And when is it allowed to change? Getting those right is most of what separates a class that survives contact with a real system from one that quietly rots.
Some things are decided once¶
An account number is chosen when the account is opened and then never again. Change it and every record referring to that account is now pointing at nothing.
Right now nothing stops it. There is no setAccountNumber, which helps, but a year from now somebody adds one because they need it for an import script, and the damage happens in a place nobody is looking.
final on a field means it is assigned exactly once, in the constructor, and after that the compiler refuses. Not a convention, not a comment. Try to reassign it anywhere and the code does not build.
Use it for identity. An account number, a customer ID, a date of birth. Anything whose whole purpose is to stay the same is a candidate, and making it final costs one word and removes a category of bug permanently.
ownerName stays non-final, because people really do change their names and the bank really does have to record it.
Objects that never change at all¶
Take that idea further. If every field of a class is final and no method changes anything, then the object cannot change after it is created. That is an immutable object.
You already use one constantly.
String name = "ada lovelace";
name.toUpperCase();
System.out.println(name); // ada lovelace, unchanged
Everybody gets caught by this once. toUpperCase did not change the string, because a String cannot be changed. It made a new one and handed it back, and you threw it away.
That is not a quirk. It is why you can hand a String to any method in the world without worrying about what it does to your copy. Nothing can do anything to it.
The same reasoning applies to anything that records a fact rather than tracking a state. A transaction record should be impossible to edit after it is written, which is exactly what an audit trail means. You will build one in Module 3.
What a fully immutable class looks like
public final class Branch {
private final String code;
private final String city;
public Branch(String code, String city) {
this.code = code;
this.city = city;
}
public String getCode() {
return code;
}
public String getCity() {
return city;
}
}
Every field final, no setters, nothing that changes anything. Hand this to any code you like. Nobody can alter it, so nobody has to check.
The class itself is final too, which stops anyone extending it and adding mutable state through the back door. That word means something you meet in week 7; for now it is a detail worth seeing rather than understanding.
Some things belong to the class¶
Now the other question. Every account has its own balance, its own number, its own owner. Three accounts, three balances.
But the bank needs unique account numbers, and the counter that produces them belongs to no single account. It belongs to all of them, which is to say it belongs to the class.
public class Account {
private static int lastNumberIssued = 1000;
private final String accountNumber;
private String ownerName;
private double balance;
public Account(String ownerName, double openingBalance) {
lastNumberIssued++;
this.accountNumber = "AC-" + lastNumberIssued;
this.ownerName = requireText(ownerName, "Owner name");
if (openingBalance < 0) {
throw new IllegalArgumentException(
"Opening balance cannot be negative: " + openingBalance);
}
this.balance = openingBalance;
}
static means there is exactly one lastNumberIssued, no matter how many accounts exist. Not one per account. One, full stop.
The constructor no longer takes an account number, because the account now issues its own. That is a real improvement: nobody outside can pick a number, so nobody outside can pick one that is already taken.
A static method belongs to the class too, and can be called without any object.
Account ada = new Account("Ada Lovelace", 500.00);
Account alan = new Account("Alan Turing", 2500.00);
System.out.println(Account.getAccountsOpened()); // 2
Called on the class, not on an object, because the answer is not about any particular account.
A static method cannot touch instance fields or use this, and once you see why, it stops feeling like a rule. There is no object involved, so there is no balance to read. Which account's balance would it be?
Remember requireText from last session. It reads no fields and writes no fields; it just checks a string. It should be static.
private static String requireText(String value, String label) {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException(label + " is required");
}
return value;
}
This also explains something that has been annoying you since first year. main is static, so it runs before any object exists, which is why you cannot call an ordinary method from it without making an object first.
The static counter will bite you in week 11
lastNumberIssued lives in memory. Close the program and it is gone. Start it again and it goes back to 1000.
So in week 11, when you save your accounts to a file and load them back, the next account you open will be given a number that already belongs to somebody. Two accounts, one number, and every lookup after that returns whichever one it happened to find first.
This is not a mistake in the code above. It is the right way to learn static and it is genuinely how many small programs work. But real systems get their identifiers from something that survives a restart, usually a database. Remember this paragraph when week 11 arrives.
Static is not a way to avoid creating objects
The first thing most people do with static is discover it makes the compiler stop complaining in main, and then mark everything static until the errors go away.
What you get is a program with one copy of everything and no objects at all. That is the pile of shared data from week 1, rebuilt on purpose, with a class name in front of it.
Before writing static, finish this sentence: this value belongs to the class because ____. If you cannot, it belongs to an object.
If you remember one thing. For every value, ask where it lives and when it may change. One per object, one for the whole class, or never again after the moment it was set.
Check yourself¶
Prove what is shared and what is not
Create three accounts without giving them numbers.
Account ada = new Account("Ada Lovelace", 500.00);
Account alan = new Account("Alan Turing", 2500.00);
Account grace = new Account("Grace Hopper", 1500.00);
System.out.println(ada);
System.out.println(alan);
System.out.println(grace);
System.out.println(Account.getAccountsOpened());
You should get three different account numbers and a count of 3. If all three accounts have the same number, lastNumberIssued is not being incremented before it is used.
Now deposit into alan only and print all three. One balance moves. The counter is shared, the balances are not, and seeing both facts in the same run is the point.
Finally, add this line and read the compiler error:
You will get two complaints, not one, and they are for different reasons. Work out what both of them are. That pair of errors is this whole session in one screen.
Think about it¶
-
The account number is
final, so it can never change. But a bank might genuinely need to renumber accounts after a merger with another bank. Doesfinalmake that impossible, or just make it obvious? Describe what you would actually have to do. -
lastNumberIssuedis shared by every account ever created, which is convenient and also means every account is coupled to every other one. Name something that could go wrong because of that sharing, other than the restart problem already described. -
A
Stringcannot be changed, so every operation on it makes a new one. That sounds wasteful. Why would the designers of Java accept that cost? Think about who receives strings and what they would otherwise have to check. -
You now have three places a value can live: in an object, in the class, or nowhere because it is computed when asked. A customer's total holdings could be any of those three. Argue for one, then find the situation that breaks your choice.