Object References and Variables
Instance variables of an object are reached with dot notation, written object_name.variable_name, following the rule that when B is part of A, its full name is A.B
The Two-Students Puzzle
Imagine two students who have the same name and the same scores on three tests. It feels natural to write one comparison and get a yes or no answer about whether they are the same student. Many beginners expect Java to work this way. It does not behave that way for objects, and the gap between expectation and behavior causes a common and persistent error. In this article you will see what Java actually compares when you use == on two objects, and you will learn a dependable way to check whether two objects hold the same data.
What do you think happens?
Two variables, std1 and std2, each refer to a different Student object. Both objects hold the name Ana and the scores 90, 85, and 77. What does std1 == std2 evaluate to?
Reveal answer
false, because the two references lead to separate objects
The == operator applied to two object references asks whether the references lead to one and the same object. It does not look inside the objects to compare their instance variables. Since std1 and std2 lead to two separate objects, the result is false even though every field holds the same value.
Reaching Instance Variables with Dot Notation
Before you can compare the contents of two objects, you need a way to reach the contents of one object. Suppose a variable named std refers to an object that is an instance of a class called Student. That object carries its own instance variables: name, test1, test2, and test3. You reach each of them by writing the object's name, a dot, and the variable's name. The variable is therefore written as std.name, std.test1, std.test2, and std.test3.
This pattern follows a naming habit you will see throughout Java. When one thing B belongs to another thing A, you write the full name of B as A.B. The dot tells you that the item on the right lives inside the item on the left. Reading std.test2 therefore means: the test2 belonging to the object that std refers to.
Notice that the dot notation always starts from a reference. The expression std.test1 does not mean a free-standing variable called test1. It means the test1 stored inside whichever object std currently refers to. This matters because two different variables can refer to two different objects, and each object has its own separate copy of the same instance variable names.
Ana
85- std is a reference that leads to one Student object
std.namereads the name stored inside that object- std.test2 reads the test2 stored inside that same object
Read an instance variable as object.variable, and always say aloud which object the reference points to. If you find yourself reading test2 with no object name in front of it, the code is referring to a different variable, not to the object's own field.
What == Actually Compares
The thing stored in std1 is a reference, a way of finding an object. The thing stored in std2 is another reference. The comparison std1 == std2 asks whether those two references lead to one and the same object. If they do, the result is true. If they lead to two separate objects, the result is false, even when every instance variable in those two objects holds identical values. The == and != operators test reference identity, not the data inside the objects.
The diagram shows the situation from the opening puzzle. The two objects have identical field values, but they are two objects. The references lead to different places, so std1 == std2 is false. The values match, but the question == answers is about whether the references lead to one object, and here they do not.
false
true
true- std1 and std2 lead to two separate objects with matching data
- std1 != std2 is the negation of the identity test, so it is true here
- std3 = std1 copies the reference, so std3 and std1 lead to the same object
Checking Each Instance Variable Separately
When the question you want answered is whether two objects hold the same data, you have to do the looking yourself. Compare each instance variable individually: the first, then the second, then the third. Then combine the results with && so that every comparison must hold before the overall answer is true. If any one field differs, the whole check fails.
truestd1.name.equals(std2.name)compares the content of the two name references- == on the int fields compares their stored numbers directly
- && requires every comparison to be true
Notice the first check. The name is a reference-type instance variable, so it is compared with equals rather than ==. If you used == on name, you would be asking whether the two name references point to one object, which is the same identity question you already learned to recognize as the wrong one here. Using equals asks whether the names have the same content. The test scores are compared with == because they are values stored directly in each object.
Comparing two students who differ in one score
std1 and std2 both have the name Ana and test1 equal to 90, but std2.test3 is 80 while std1.test3 is 77. Does the data check report that they hold the same data?
- 1Check the name
std1.name.equals(std2.name)is true, because both names contain Ana. - 2Check test1
std1.test1 == std2.test1 is true, because both are 90.
- 3Check test2
std1.test2 == std2.test2 is true, because both are 85.
- 4Check test3
std1.test3 == std2.test3 is false, because 77 is not 80.
- 5Combine with &&
Since one part of the && chain is false, the whole expression is false.
The data check returns false. The objects do not hold the same data, even though the name and two of the three scores match.
| Question asked | How it is written | What it checks | Result for two matching students |
|---|---|---|---|
| Are these the same object? | std1 == std2 | Whether both references lead to one object | false |
| Are these different objects? | std1 != std2 | Whether the references lead to different objects | true |
| Do these hold the same data? | field-by-field comparison joined with && | Whether every instance variable matches | true |
Identity checks and data checks answer different questions
Mistakes When Comparing Objects
Common mistake Expecting std1 == std2 to confirm that two students match
What it looks likestd1 == std2 printed false even though both students have name Ana and the same scores.Why it failsThe operator compares the references, which lead to two separate objects, not the instance variables inside them.
Do this insteadCompare each instance variable individually, joined with &&, when you want to test whether the data matches.
Common mistake Using == on a reference-type instance variable such as name
What it looks likestd1.name == std2.nameWhy it failsThis asks whether the two name references point to one object, not whether the names have the same content.
Do this insteadUse std1.name.equals(std2.name) to compare the content of the names.
Common mistake Forgetting the object name when reading an instance variable
What it looks likeReading test2 alone when you meant std.test2
Why it failsThe dot notation starts from a reference. Without the reference, the expression does not identify the object's own field.
Do this insteadWrite the full name object.variable, such as std.test2.
Common mistake Combining field checks with || instead of &&
Why it failsWith || a single matching field would make the whole check true, even when other fields differ.
Do this insteadJoin the individual comparisons with && so that every one must hold.
Practice Comparing Two Objects
Two Student references, a and b, each refer to a different object. Write a single boolean expression that is true only when the names match and test1, test2, and test3 all match. Then predict what your expression returns when a.name is Ana and b.name is Ana but b.test3 holds a different value from a.test3.
Hints
- Compare name with equals, not with ==.
- Use one comparison per instance variable and join them with &&.
- A single false inside a && chain makes the whole expression false.
Key Takeaways
- Instance variables are reached with dot notation as
object_name.variable_name, following the rule that when B is part of A, its full name is A.B. - The == and != operators applied to two object references test whether the references point to the same object, not whether the objects' instance variables match.
- To test whether two objects hold the same data, compare each instance variable individually and join the comparisons with && so that every one must hold.
- Reference-type instance variables such as name should be compared with equals when you want to check their content.