Creating and using objects
On This Lesson 11 topics
Lesson Overview
You can now write a class with fields, constructors, and methods. This lesson is about the other side: using the class from main or any other code. You will build objects with new, call their methods, and, most importantly, understand what a variable of a class type really holds.
The key idea is that a variable like Account ana does not contain the account. It contains a reference: an arrow, or address, that points to the object. In the blueprint picture, the variable is a slip of paper with a house address on it. Copying the slip does not build a second house.
Why this matters: references explain three things that confuse most beginners: why changing an object through one variable changes it for another, why == does not compare contents, and what a NullPointerException is. All three appear daily in backend code.
This lesson builds on Lessons 1 to 3 and on arrays and stack traces from earlier modules.
Learning Objectives
By the end of this lesson you will be able to:
- Create objects with
newand call their methods and fields with the dot. - Explain that an object variable holds a reference, not the object.
- Predict what happens when one reference is copied to another variable.
- Explain why
==on two objects compares references, not contents. - Explain
nulland diagnose aNullPointerException. - Create arrays of objects and loop over them, remembering that the slots start as
null.
Written Explanation
Creating and using
A complete use of a class has three parts: declare a variable, create the object with new, and use it through the dot.
Account ana = new Account("Ana", "A-1", 100);
ana.deposit(50);
System.out.println(ana.describe());new Account(...) creates the object and runs the constructor. According to the Java tutorial, new allocates memory for the object and returns a reference to it. The assignment stores that reference in ana. After that, ana.deposit(50) means "follow the reference and run deposit on the object found there".
The variable declaration and the creation are separate things. Account x; alone creates no object. The variable x exists, but it points to nothing, and if it is a local variable, Java will not even let you use it before it is assigned:
U.java:1: error: variable x might not have been initializedYou do not always have to store the reference. new Account("Tmp", "T-0", 1).deposit(5); creates an object and uses it once. Nobody can reach it afterwards, so this is useful only for rare one-off calls.
Two variables, one object
Because the variable holds a reference, copying the variable copies the reference:
Account ana = new Account("Ana", "A-1", 100);
Account alias = ana;
alias.deposit(25);
System.out.println(ana.balance);This prints 125.0. There is one account object, with two names for it. alias.deposit(25) followed the arrow to that single object. This is the picture to keep in your head:
ana ----\
+--> [ Account: Ana, A-1, 125.0 ]
alias ----/Reassigning a variable only moves its arrow and does not change the object. After alias = new Account("Bea", "B-1", 0);, alias points to a new object, and ana still points to the old one. Primitives behave differently: int a = 5; int b = a; copies the number itself, so changing b never affects a. That is why object variables need extra care.
The same holds when you pass an object to a method. The parameter is a copy of the reference, so the method can change the object that the caller sees:
static void restockLikeThis(Product p) {
p.stock = 100; // changes the caller's object
}(The method cannot, however, make the caller's variable point somewhere else.)
== compares references
For objects, == asks "do both variables point to the same object?", not "do the objects hold the same data":
Account twin = new Account("Ana", "A-1", 125);
System.out.println(ana == alias); // true: same object
System.out.println(ana == twin); // false: two objects, equal contentsEqual-looking contents do not make == true. Later modules teach how to define "equal contents" with equals; for now, remember that == on objects is about identity. You already met this with String in the arrays and strings module.
null: pointing at nothing
null is the reference that points to no object. It is the default for every object-typed field, and you can assign it yourself. You can store it, compare it (x == null), and print it, but you cannot follow it. Calling a method or reading a field through null throws NullPointerException. The Java documentation lists exactly these situations: calling an instance method on null, accessing a field of null, and using null as an array.
Product c = new Product("Tea", 2.5, 10);
c = null;
System.out.println(c.stock);Exception in thread "main" java.lang.NullPointerException: Cannot read field "stock" because "c" is null
at Main.main(Main.java:14)Modern JDKs describe what was null in the message. Combine it with the line number in the stack trace and you can find the cause quickly. (Names such as c appear when the code is compiled with debugging information, which IDEs normally do; otherwise you may see placeholders like <local4>.) The fix is never to catch the exception and move on: find out why the variable was null and make sure it holds an object, or check if (c != null) where null is a legitimate state.
Arrays of objects
An array of objects is an array of references. Creating the array does not create any objects:
Account[] all = new Account[2]; // two slots, both null
all[0] = new Account("Omar", "A-2", 80);
System.out.println(all[0].describe()); // works
System.out.println(all[1].describe()); // NullPointerException: all[1] is nullEach slot must be filled with new before use. When every slot is filled, a loop works as always: for (Account acc : all) { acc.deposit(10); }.
Examples
Example 1: two accounts, independent (simple)
Account ana = new Account("Ana", "A-1", 100);
Account omar = new Account("Omar", "A-2", 80);
ana.deposit(50);
System.out.println(ana.describe());
System.out.println(omar.describe());Output (JDK 21, using the Account class from Lesson 3):
Ana (A-1): 150.0
Omar (A-2): 80.0Two new calls, two objects, two separate balances.
Example 2: aliasing and identity (practical)
Product a = new Product("Tea", 2.5, 10);
Product b = a;
Product c = new Product("Tea", 2.5, 10);
b.sell(3);
System.out.println(a.stock + " " + c.stock);Output:
7 10b is a second name for the object a points to. Selling 3 through b lowered the stock seen through a. But c is a separate object created with the same values, so its stock is untouched. Here a == b is true and a == c is false, even though a and c hold identical data.
This is also the reason a function that receives a Product can change the caller's product. That is sometimes exactly what you want (a restock helper) and sometimes a surprise, so be deliberate about it.
Example 3: a list of accounts and a null slot (realistic)
A small program keeps its accounts in an array and applies a bonus to each:
Account[] arr = {
new Account("A", "1", 1),
new Account("B", "2", 2)
};
for (Account acc : arr) {
acc.deposit(10);
System.out.println(acc.describe());
}Output:
A (1): 11.0
B (2): 12.0This works because both slots hold objects. Had we written new Account[2] and filled only slot 0, the loop would have thrown a NullPointerException on the empty slot, just as all[1] did in the section above. When arrays are only partly filled, the loop needs a guard: if (acc != null). Real programs avoid the problem by filling the array completely, or by using collections, which a later module covers.
Backend Insight
Almost everything in a backend is a reference to an object, and objects are often shared. A cache may hand the same Product object to two requests at the same time, and if one request changes it, the other sees the change. Knowing that variables are arrows tells you to stop and think whenever you hand an object to another piece of code: "Do I share this one, or give a separate copy?"
NullPointerException is the most common runtime error in Java services. It usually means a value that "should be there" was not: a record not found in a database, a field of a request left out, a lookup that returned nothing. Reading the message and the first line of the stack trace that belongs to your code, as you learned in the debugging module, is the fastest way to find it. Over time you will learn patterns for expressing "might be absent" more safely, but understanding what null is comes first.
Common Mistakes
Mistake 1: Thinking b = a copies the object
- Incorrect: "
Account b = a;makes a second account". - Correct: it makes a second reference to the same account. To get a second object, use
new.
Mistake 2: Comparing objects with == to check their contents
ana == twin is false even if every field matches. == only tells you whether two variables point to the same object.
Mistake 3: Calling a method on null
Fields default to null, array slots start null, and lookups can return null. Read the exception message, find the variable it names, and decide where the object should have been created.
Mistake 4: Creating an array and assuming it contains objects
new Account[3] makes three empty slots. Fill each with new Account(...) before using it.
Mistake 5: Forgetting that a method can change the caller's object
Passing an object to a method hands over the reference. Changes the method makes to the object's fields are visible to the caller.
Exercises
Try each exercise yourself before opening the solutions. We continue with Product (constructor with name, price, stock; sell(int) returning boolean).
Level A: Foundation
A1. After Product p = new Product("Tea", 2.5, 10); and Product q = p;, how many Product objects exist, and how many references?
Show solution A1
A1. One object and two references (p and q). Only new creates objects.
A2. What exception does Product r = null; r.sell(1); throw?
Show solution A2
A2. NullPointerException: you cannot call an instance method on null.
A3. What does new Product[4] contain right after it is created?
Show solution A3
A3. An array of four null references. No Product object exists yet.
Level B: Application
B1. Predict the output, then explain the second line:
Product x = new Product("Mug", 6, 5);
Product y = x;
y.sell(2);
System.out.println(x.stock);
System.out.println(x == y);Show solution B1
B1. It prints 3 and then true. y and x refer to the same object, so selling 2 through y reduced the stock seen through x from 5 to 3. x == y is true because both variables point to the very same object.
B2. Write a static void clearStock(Product p) in Main that sets a product's stock to 0. After calling it with a product that has stock 10, what does the caller see? Why?
Show solution B2
B2.
static void clearStock(Product p) {
p.stock = 0;
}The caller sees stock 0. The parameter p is a copy of the reference, so it points to the caller's object, and the assignment changes that object's field.
B3. Complete the code so that it prints the name of each product in the array without throwing an exception:
Product[] shelf = new Product[3];
shelf[0] = new Product("Tea", 2.5, 10);
shelf[2] = new Product("Mug", 6, 3);
// loop hereShow solution B3
B3.
for (Product p : shelf) {
if (p != null) {
System.out.println(p.name);
}
}This prints Tea and Mug and skips the empty slot shelf[1], which would otherwise cause a NullPointerException.
Level C: Challenge
C1. Two Product objects have the same name, price, and stock. Explain why p1 == p2 is false, and describe in words what you would need to compare to decide they are "equal".
Show solution C1
C1. p1 and p2 are two separate objects, so the references differ and == is false. To decide they are equal in content, you would compare the field values one by one (name, price, stock), or later define an equals method that does so.
C2. A Main stores three accounts in an array and then calls transferAll(Account[] list, Account target), a method that withdraws the full balance of every account in list and deposits it into target. Write the method and explain what happens if target is also in the list (use the withdraw and deposit of Lesson 3).
Show solution C2
C2.
static void transferAll(Account[] list, Account target) {
for (Account a : list) {
double amount = a.balance;
if (a.withdraw(amount)) {
target.deposit(amount);
}
}
}If target is in the list, then when the loop reaches it, it withdraws its own balance (now including earlier deposits) and deposits it back, so its balance ends up unchanged for that step. The result is still sensible here, but it shows that the same object may be reachable under different names, so code that handles several objects should consider that a "different" variable can still be the same account.
Mini Challenge
Using your own class (or Account), write a Main that does the following and prints the evidence for each point:
- Creates two objects with
newand one extra variable that is an alias of the first. - Changes the first object through the alias and shows the change through the original name.
- Shows
==between the alias and the original (true) and between the original and a lookalike you create (false). - Creates an array of three, fills only two slots, and loops over it safely with a
nullcheck.
Then remove the null check, run it, and read the exception message and the line it points to.
Check your work: you can explain each printed value with the "slip of paper with an address" picture.
Summary & Next Lesson
Key takeaways
newcreates an object, runs its constructor, and returns a reference; the variable stores the reference.- Copying an object variable copies the reference, so two variables can point to one object. Use
newto make another object. ==on objects compares references (identity), not contents.nullmeans no object. Using it to call a method or read a field throwsNullPointerException; the message and stack trace tell you which variable and which line.- A new array of objects holds
nullin every slot until you fill it.
Next lesson
Every object so far has had its own copy of every field. The next lesson, "static: belonging to the class", looks at members that exist once for the whole class, and how to choose between instance and static.
Self-check
- What does an object variable hold?
- After
Account b = a;, how many objects exist? - What does
==compare for two objects? - When is a
NullPointerExceptionthrown? - What is in each slot of
new Account[3]?
Show answers
- A reference (the address) to the object, or
null. - Still just the one that
apoints to; there are two references. - Whether both variables refer to the same object.
- When you call a method on, read a field of, or index into a
nullreference. null.
References
- Oracle, The Java Tutorials, Creating Objects. Supports:
newallocates memory and returns a reference, a declared variable does not create an object, and several references can point to the same object. - Oracle, Java SE 21 API, NullPointerException. Supports: the list of situations that throw it (calling an instance method of
null, accessing a field ofnull, treatingnullas an array). - Output and error messages were produced on JDK 21 (javac 21.0.10). The description of
<local4>-style placeholders reflects what we saw when compiling without debug information; this is a javac option behaviour we did not look up in a reference. - The pass-by-value-of-the-reference description (a method receives a copy of the reference) was demonstrated by running the code but not cited from the language specification.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.