- Learn
- Computer Science & IT
- Programming
- Object-Oriented Programming with Java
- Java Objects and References
Objects, References and Equality in Java
null, pass objects to methods and return them, and build classes from other objects and from lists of objects. Then write equals and hashCode so that lists, sets and maps compare your objects by content, and keep a program's text interface separate from its logic.BeginnerLesson 6 of 11 in Object-Oriented Programming with Java Course
What you will learn
- Explain what a Java variable holds for primitive and reference types, predict what assignment does to each, and handle variables that refer to no object.
- Compare primitive and reference variablesName Java's primitive types, explain that a reference variable holds a reference to an object, and predict the effect of copying each kind of variable.
- Trace aliases, reassignment and unreachable objectsFollow several variables that refer to one object, tell changing an object apart from pointing a variable at another object, and explain what happens to objects that nothing refers to.
- Work safely with the null referenceExplain what null means, recognize where it appears, prevent NullPointerException and decide when null is a reasonable value.
- Compare primitive and reference variables
- Explain how Java passes arguments, write methods that receive and change objects or compare them with another object of the same class, and return new or existing objects safely.
- Use objects as method parametersPredict what a method can and cannot change when it receives a primitive or a reference, and write methods that take objects of another class or of their own class.
- Return objects from methodsWrite methods that create and return new objects, return references to objects that already exist, and avoid exposing an object's internal state by accident.
- Use objects as method parameters
- Design classes whose fields are other objects or lists of objects, work with lists that hold objects, and decide when contained objects should be shared or copied.
- Compose a class from other objectsModel has-a relationships with object fields, delegate work to the contained objects, and decide whether a contained object should be shared or copied.
- Store and process objects in listsAdd, find, change and remove objects in an ArrayList, and explain why the list holds references rather than copies.
- Build a class around a list of objectsWrite a class that keeps a list of objects as a field, initializes it, offers methods for working with its contents and keeps the list itself hidden.
- Compose a class from other objects
- Tell identity apart from equal content, write correct equals and hashCode methods, and predict how lists, sets and maps use them.
- Distinguish identity from equal contentExplain what == and equals compare for primitives, strings, wrappers and your own classes, and predict the result of each comparison.
- Write an equals methodOverride equals with the correct signature and steps, compare each kind of field correctly, and keep the method consistent with its general contract.
- Pair hashCode with equalsExplain the hashCode contract, write hashCode for a class that overrides equals, and predict how HashSet and HashMap behave with and without it.
- Distinguish identity from equal content
- Structure a text-based program so that one class talks to the user and other classes hold the data and rules, then extend and check it without mixing the two.
- Split a program into a user interface and logic classesDecide which code belongs in the user interface and which in the logic, and connect them through constructor parameters and a start method.
- Extend and check a separated programAdd features by extending the logic and calling it from the interface, check the logic directly with known values, and keep each class focused on one job.
- Split a program into a user interface and logic classes
Java Objects and References study guide
1Follow values and references
A variable and an object are different parts of a Java program. Understanding what each variable holds lets you explain why one assignment creates independent numbers while another gives two names access to the same object. Start with the values being copied, then trace changes one statement at a time. The same picture will help you find missing objects, read null errors and decide whether a change should be shared.
1.1 What a variable holds
A Java variable holds a value. The important question is whether that value is ordinary data or a reference that leads to an object. Assignment copies that value in both cases.
Primitive data and object references
Java has eight primitive types: byte, short, int, long, float, double, char and boolean. A primitive variable holds its data directly. A reference variable holds a reference to an object or the special value null. Classes such as String, Scanner and ArrayList, your own classes, and arrays are reference types.
| Declaration | What the variable holds |
|---|---|
int count = 5; | The integer value 5 |
Counter c = new Counter(); | A reference to a new counter |
Counter c = null; | No object reference |
In new Counter(), Java creates an object, runs its constructor and produces a reference. The assignment stores that reference in c. Draw the variable as a box with an arrow leading to a separate object. Each evaluation of new creates a separate object, even when the arguments match.
Copy the value, then follow the result
With primitives, changing the copied variable leaves the first variable alone:
int a = 5;
int b = a;
b = b + 1;
The last line reads 5 from b, adds 1 and stores 6 in b. The value in a remains 5.
For a reference example, use this small class:
class Counter {
private int value;
Counter() { this.value = 0; }
Counter(int value) { this.value = value; }
void increase() { value++; }
int value() { return value; }
}
Now trace these statements:
Counter first = new Counter();
Counter second = first;
second.increase();
System.out.println(first.value()); // 1
Only the first line creates a counter. The second line copies the reference, so the method call changes the one counter that both variables reach. To make an independent counter with matching data, write new Counter(first.value()). Copying a variable does not call a constructor.
Wrappers and immutable strings
Every primitive type has a wrapper class, such as Integer, Double, Boolean or Character. Wrapper variables hold references or null. Java can convert automatically between a primitive and its wrapper; these conversions are autoboxing and unboxing. Lists hold references, so a list of integers is ArrayList<Integer>.
String is also a reference type, but its objects are immutable: their characters cannot change after creation.
String s = "java";
String t = s;
t = t.toUpperCase();
Here s still refers to "java" and t refers to "JAVA". The last line assigns the result to t; it does not change the shared original string. String methods may reuse the original object when no change is needed.
Printing an object uses its toString() method. Without an override, the inherited result has a form such as Counter@7e9e5f8a: class name, @ and a hexadecimal hash code. Define toString() when printed output should show the object's data.
Key Takeaways
- Assignment copies a value: primitive data or an object reference.
- Every evaluation of
newcreates a separate object. - Copying a reference shares the object; it does not copy its fields.
- Wrappers and strings are reference types; strings are immutable.
- Printing an object uses
toString().
1.2 Shared objects and changing arrows
Two variables that refer to the same object are aliases. An alias gives another route to existing data. To trace a program, keep the variables separate from the objects they reach.
Mutation changes data; reassignment changes a variable
A mutation changes an object's state, such as its stored weight. A reassignment puts a different value into a variable. For a reference variable, that changes its arrow.
Use a box whose weight is in kilograms:
class Box {
private double weight;
Box(double weight) { this.weight = weight; }
double getWeight() { return weight; }
void setWeight(double weight) { this.weight = weight; }
}
Follow this sequence:
Box a = new Box(2);
Box b = a;
b.setWeight(5);
b = new Box(7);
- Draw variable
apointing to object A, with weight 2. - Draw
bpointing to that same object A. There is still one box. - Change the weight inside object A to 5. Both routes now reach weight 5.
- Create object B with weight 7 and redirect only
bto it.
At the end, a.getWeight() is 5 and b.getWeight() is 7. The fourth statement neither resets object A nor moves a to object B.
| Action | Change in the diagram | Effect through other aliases |
|---|---|---|
b.setWeight(5) | Change a field inside the object | They see the new weight |
b = new Box(7) | Redirect the arrow from b | They keep their existing arrows |
b = null | Remove the arrow from b | They still reach their objects |
Reachability controls an object's lifetime
An object is reachable while the running program can follow some usable chain of references to it. That chain may run through a variable, an object field or a list element. A local variable disappearing is therefore not enough to make an object unreachable if a list still holds it.
Continue the trace with a = null;. Object A, weighing 5 kg, now has no reachable path in this example. It is eligible for garbage collection. Java automatically reclaims memory from unreachable objects, but the collector chooses when; the assignment does not promise immediate collection. Object B remains reachable through b.
You do not free Java objects by hand. When a program loses its last usable reference to an object, it also loses the ability to inspect or change that object's data.
Find the hidden aliases
Aliases arise when an object is added to a list, passed to a method, returned from a method or stored in another object's field. Each operation can pass on a reference. Before mutating an object received from elsewhere, identify which other code can still reach it.
A common bug starts with the thought, "I copied the variable, so I have my own box." Use a fresh object when changes should be independent. To diagnose the bug, count object creations separately from assignments, label each object once, and process every statement in order. Do not redraw an object just because a second variable now points to it.
Key Takeaways
- Aliases lead to one object, so they share its mutations.
- Reassignment redirects one variable and leaves other aliases alone.
- Trace fields and reference arrows separately.
- An unreachable object is eligible for garbage collection; timing is unspecified.
1.3 No object: null and safe checks
The value null means that a reference leads to no object. It is useful for an absent result, but a program must handle that absence before it tries to use an object.
Missing is different from empty
String text = ""; refers to a real string with length 0. String text = null; refers to no string, so there is no object on which to call length().
A primitive cannot hold null: int x = null; does not compile. A wrapper can hold it: Integer x = null; compiles. Converting that wrapper to a primitive with int y = x; throws a NullPointerException, because unboxing needs a wrapper object.
Fields get initial default values when an object is created. Reference fields start as null, numeric fields as 0 and boolean fields as false, until initialization assigns other values. Local variables have no default. Reading a local before assigning it causes a compile error, such as "variable might not have been initialized".
Find the missing reference
Calling an instance method through null, accessing an instance field through it or reading an array's length through it throws NullPointerException at runtime. For example:
String name = null;
System.out.println(name.length());
Read the exception's line number, then identify which reference on that line was missing. Helpful messages are enabled by default from Java 15. A message may say Cannot invoke "SimpleDate.getYear()" because "this.birthday" is null. Local names may appear as placeholders such as <local4>, depending on compilation details. Trace back to the constructor, assignment or search that should have supplied the object.
Printing a String variable whose value is null prints "null"; concatenating it produces text such as "Hi null". These operations do not need to call an instance method through that variable. They can reveal missing data before a later method call fails.
Check before using
The operator && short-circuits: it evaluates the right side only if the left side is true.
if (name != null && name.length() > 0) {
System.out.println(name);
}
When name is null, the first test is false and length() is skipped. Reversing the two tests loses that protection: the method call would run first.
| Purpose | Safe expression |
|---|---|
| Check absence | name == null |
| Check presence | name != null |
| Match a fixed command | "quit".equals(command) |
The fixed string is a known object, so its equals can receive null and return false. command.equals("quit") fails when command is null. Likewise, x.equals(null) is no way to check whether x exists: it already requires an object.
Make absence a clear design choice
Initialize a list field to new ArrayList<>() so that an empty collection is available immediately. If a search returns null for "not found", document that result and check it at the call site. Avoid filling every field with arbitrary objects merely to silence an exception: supply the object the program needs, or give absence a clear meaning that the calling code handles.
Key Takeaways
nullmeans no object; an empty string is a usable object.- Fields receive defaults, but locals must be assigned before reading.
- Null dereferencing and unboxing a null wrapper fail at runtime.
- Put the null check before the member access in
&&. - Call fixed-text comparisons on the known string.
- Initialize collections and handle absent search results deliberately.
2Use objects across method calls
Methods let objects work together. A call can read or change a shared object, create an independent result, or return an object already held elsewhere. These choices affect what the caller sees after the call. Follow the parameter and return references explicitly, and put comparisons in the class that owns the data. Then decide which methods should share existing state and which should protect it with a copy.
2.1 Parameters receive copied values
Java passes every argument by value. A parameter is a new variable that receives a copy of the argument's value. For an object argument, the copied value is a reference.
Change the parameter or change the object?
A primitive parameter has its own data:
static void addTen(int n) {
n = n + 10;
}
If x is 5, calling addTen(x) leaves x at 5. Only the local parameter n becomes 15.
A reference parameter can reach the same object as the caller. Suppose Person.growOlder() increases the stored age:
static void celebrate(Person p) {
p.growOlder();
}
static void replace(Person p) {
p = new Person("Bo", 30);
}
During celebrate(ana), the parameter p and variable ana are aliases. The changed age remains visible afterward. During replace(ana), assigning p redirects only that parameter. The caller's ana still refers to the original person. A method that swaps two reference parameters has the same limit: it swaps its own copies, not the caller's variables.
| Inside a method | Caller observes |
|---|---|
| Assign a primitive parameter | No change to the caller's variable |
| Mutate the parameter's object | Changed state of the shared object |
| Reassign a reference parameter | No change to the caller's variable |
Receive another class's object
A gate can read a person's height and keep its own count of checks. This class expects a non-null person with a getHeight() method returning centimeters:
class HeightGate {
private int minHeight;
private int checks;
HeightGate(int minHeight) {
this.minHeight = minHeight;
}
boolean allowIn(Person p) {
checks++;
return p.getHeight() >= minHeight;
}
int checks() { return checks; }
}
For a gate set to 120 cm, checking people of 132 cm and 115 cm returns true and false. The gate then reports 2 checks. It reads the person's height through a public method and changes its own counter.
Receive another object of the same class
Inside SimpleDate.before(SimpleDate other), this is the date receiving the call and other is the argument. A class can read the private fields of any object of that same class.
For fields day, month and year, compare in order:
boolean before(SimpleDate other) {
if (year != other.year) {
return year < other.year;
}
if (month != other.month) {
return month < other.month;
}
return day < other.day;
}
For 14 March 2001 and 2 November 2001, years match and month 3 is less than month 11, so the first date is earlier. Equal dates return false because neither is before the other.
Person.olderThan(other) can delegate to birthday.before(other.birthday). The date comparison belongs with the date data. Methods that call instance methods on a parameter need a non-null argument; either check it or make that requirement part of the method's documented use.
Key Takeaways
- Every parameter receives a copied value, including reference parameters.
- Mutation can affect a shared object; parameter reassignment cannot replace the caller's variable.
thisnames the receiver; an object parameter names the argument.- A class can access private fields in another object of the same class.
- Delegate comparisons to the class that owns the relevant data.
2.2 Returning a new object or an existing one
An object-returning method returns a reference. Its type tells you what kind of object can be returned, but it does not tell you whether the object is new, shared or absent. The method's purpose must make that choice clear.
Create a result
A factory method supplies an object. It can create one or reuse an existing one. Here each call deliberately creates a separate ticket:
class TicketMachine {
private int nextNumber = 1;
Ticket issue() {
Ticket t = new Ticket(nextNumber);
nextNumber++;
return t;
}
}
The machine expects a Ticket constructor that stores its number. Three calls create three tickets numbered 1, 2 and 3. Returning a local variable is safe: its reference is copied to the caller, which can keep the object reachable after the local variable goes out of scope.
A copy method promises an independent object. In the Counter class:
Counter copy() {
return new Counter(this.value);
}
After Counter a = new Counter(4); Counter b = a.copy(); b.increase();, a.value() is 4 and b.value() is 5. Replacing a.copy() with a would share the counter.
A method can also build a computed object. For a Point with double fields x and y:
Point midpoint(Point other) {
return new Point((x + other.x) / 2.0,
(y + other.y) / 2.0);
}
Points (2, 4) and (6, 10) give (4.0, 7.0). The method computes each coordinate and passes both to a new point; neither original point changes.
Return an object already stored
A register's findByName should return the matching stored person if callers need to work with that entry. Creating new Person(name, 0) would return invented replacement data and disconnect later changes from the register.
A search may return null when no match exists:
Person p = register.findByName("Lin");
if (p != null) {
System.out.println(p);
} else {
System.out.println("Not found");
}
If p is the stored person, changes through p also change the person reached by the register. A return statement does not copy the object.
Keep mutable internal state under control
Suppose Person.getBirthday() returns its SimpleDate field and that date has setters. A caller can then change the person's birthday without calling a Person method. This is an exposure of mutable state.
To protect the field, return a new date built from its values:
SimpleDate getBirthday() {
return new SimpleDate(birthday.getDay(),
birthday.getMonth(),
birthday.getYear());
}
This version requires a birthday object to have been initialized. Other designs use an immutable date or offer specific queries without a date getter. Choose the behavior for each method: find normally shares a stored entry, while copy creates independence. A getter's short name alone is no promise of protection.
Key Takeaways
- An object return passes a reference to the caller.
- Factories may create or reuse objects; a copy method creates a separate object.
- Search methods can share existing entries and must define an absent result.
- Returning a mutable field can let callers bypass the owner's methods.
3Connect objects and lists
A larger model is a network of objects: a person has a date, a playlist has a list, and the list holds songs. Each connection is a reference with consequences for sharing and change. Build those connections deliberately, delegate work to the object that understands it, and keep collection rules behind useful methods. A copied container can protect membership while still sharing every object inside it.
3.1 Objects with object fields
Composition builds a class using fields whose types are other classes. It expresses a "has a" relationship: a person has a birthday; a course has a teacher. The outer object stores a reference to the contained object.
Create and connect the objects
A constructor can receive an existing date:
class Person {
private String name;
private SimpleDate birthday;
Person(String name, SimpleDate birthday) {
this.name = name;
this.birthday = birthday;
}
boolean olderThan(Person other) {
return birthday.before(other.birthday);
}
public String toString() {
return name + ", born " + birthday;
}
}
Calling new Person("Ana", new SimpleDate(14, 3, 2001)) first creates a date and then gives its reference to the person. Alternatively, a person constructor can receive day, month and year and create the date inside its body. With that approach, the caller does not retain a reference to that newly created date.
Delegate work to the contained object
The call birthday.before(other.birthday) asks the date object to compare dates. Person does not need to repeat the year-month-day comparisons. Concatenation in toString() also delegates: Java calls the birthday object's toString() to obtain its text.
A date object can help compute age. Start with the current year minus the birth year. Then form this year's birthday and subtract one if the current date comes before it. A person born on 14 March 2001 is 24 on 1 January 2026: the year difference is 25, but the birthday has not arrived. On 14 March 2026, the birthday has arrived and the age is 25. Keeping the date comparison in SimpleDate avoids reproducing its rules in every person method.
Decide whether the inner object is shared
The constructor above stores its argument directly. If two people receive one mutable SimpleDate, both birthday fields point to the same date. Changing that date affects both people.
| Constructor choice | Reference consequence |
|---|---|
| Store the incoming date reference | Caller and person may share the date |
| Build a new date from incoming values | Person begins with its own date |
| Store an immutable date | Sharing cannot change its date values |
A defensive constructor can copy the incoming date's day, month and year. An immutable date has no state-changing methods and keeps its values fixed; simple primitive fields can be final. Copying on input matters even if a getter later returns a copy, because the original caller may still hold the incoming object.
Handle optional connections
A Course might have no teacher yet. If that is a meaningful state, teacherName() can check the field and return "no teacher yet". Otherwise, require a teacher at construction.
In course.getTeacher().getName(), each instance call needs an object: a null course fails at getTeacher(), and a null teacher fails at getName(). A method on the course can keep that absence check in one place and avoid making every caller know the same chain.
Key Takeaways
- Composition connects an outer object to other objects through fields.
- Delegate operations to the object that owns the relevant data.
- Storing an incoming mutable reference can share state with the caller.
- Copies or immutable inner objects can protect independent state.
- Optional object fields need a clear null-handling policy.
3.2 Lists hold references to their elements
An ArrayList<Person> is an object whose elements hold references to people. The list controls its sequence of entries; each person controls its own fields. These are separate kinds of change.
Add an object and reach it again
ArrayList<Person> people = new ArrayList<>();
people.add(ana);
people.get(0).growOlder();
This code needs import java.util.ArrayList; and an existing non-null person ana. add copies the reference. The growOlder() call reaches the same person as ana, so the age change is visible through either route. The list never creates a second person here.
You can create an object while adding it: people.add(new Person("Lin", 1998));, using a person constructor that accepts a name and birth year. The list then gives the program a route to that new person through get or a loop. List type arguments must be reference types, so use ArrayList<Integer> for whole numbers.
Compute from objects and select an object
Suppose a list holds parcels with weights 2.5 kg, 4.0 kg and 1.5 kg. A loop adds each parcel's weight:
double total = 0;
for (Parcel p : parcels) {
total += p.getWeight();
}
The running totals are 2.5, 6.5 and 8.0 kg. Selecting the heaviest parcel needs a reference as well as a comparison:
Parcel heaviest = parcels.get(0);
for (Parcel p : parcels) {
if (p.getWeight() > heaviest.getWeight()) {
heaviest = p;
}
}
This selection expects a nonempty list. The result is the 4.0 kg parcel object, so the caller can use its other methods too. For an empty list, decide on an absent result before reading element 0.
Search every entry
A name search in a class that stores people can use:
Person findByName(String name) {
for (Person p : people) {
if (p.getName().equals(name)) {
return p;
}
}
return null;
}
This version expects non-null people with non-null names. The first match is returned immediately. The final return null belongs after the loop: a failed comparison on the first person does not rule out later matches.
contains, indexOf and remove(Object) search using equals. If Person inherits identity-based equality, a newly constructed person will not match an existing one just because their fields match.
Remove membership without destroying an object
One object can appear twice in one list or in several lists. Removing an entry does not destroy the object or remove other references to it.
Do not remove entries directly from the same list during a for-each traversal. It can trigger ConcurrentModificationException. Collect candidates first, then remove them:
ArrayList<Person> toRemove = new ArrayList<>();
for (Person p : people) {
if (p.getName().equals("Lin")) {
toRemove.add(p);
}
}
people.removeAll(toRemove);
The traversal finishes before the list's membership changes. removeAll uses equality, so it removes entries equal to the collected candidates.
Key Takeaways
- Adding to a list shares an object reference.
- A loop can compute a total or retain a reference to a selected object.
- A failed search returns only after all candidates have been checked.
- List membership operations depend on
equals. - Removing an entry leaves other references intact.
- Collect removals first and change the list after traversal.
3.3 A class that owns a list
A class can keep a list in a private field and offer operations that describe its purpose. A playlist stores songs, counts them and calculates duration. Callers should not need to manage its list directly.
Initialize and wrap the collection
This playlist expects non-null Song objects with a seconds() method:
import java.util.ArrayList;
class Playlist {
private String name;
private ArrayList<Song> songs;
Playlist(String name) {
this.name = name;
this.songs = new ArrayList<>();
}
void add(Song song) { songs.add(song); }
int count() { return songs.size(); }
int totalSeconds() {
int total = 0;
for (Song song : songs) {
total += song.seconds();
}
return total;
}
}
Without new ArrayList<>(), the reference field remains null; the first call to add then fails. An empty list is already a usable object, with size 0.
For songs lasting 210 s, 185 s and 247 s, count() returns 3. The total grows from 210 to 395 to 642 s. Integer division by 60 gives 10 minutes, with remainder 42 seconds. A longest() method can select and return the 247 s song, using the same reference-selection pattern as the heaviest parcel. Define an absent result for an empty playlist.
Protect membership through methods
Keeping songs private blocks direct field access, but return songs; exposes that very list. A caller could call clear() and bypass every playlist rule.
Specific operations such as add, count and totalSeconds keep responsibility with the playlist. Its add method could enforce a maximum size or refuse duplicates with songs.contains(song). Content-based duplicate checks need the song class's equals to define which fields make two songs equal.
When a constructor receives an initial list, copy its structure with this.songs = new ArrayList<>(initialSongs);. Storing the parameter directly would let the caller keep an alias to the playlist's internal list.
A new list can still share songs
A query can return new ArrayList<>(songs). This is a shallow copy: a new list object, containing the existing song references.
| Caller action on the returned copy | Effect on the playlist |
|---|---|
| Remove an entry | Playlist membership stays the same |
| Clear the copy | Playlist membership stays the same |
| Mutate a shared song | Playlist sees that song's changed state |
A songsLongerThan(200) query creates a fresh result list, loops over the stored songs and adds those whose duration exceeds 200 s. For the three songs above it returns the 210 s and 247 s songs. When song values must also be independent, copy the mutable songs or make Song immutable.
Key Takeaways
- An owner object, its list and its elements are separate objects.
- Initialize the list before using it.
- Offer operations that enforce the owner's collection rules.
- Copy incoming lists to prevent an outside alias to internal membership.
- A shallow list copy shares its element objects.
4Define what makes objects equal
Two objects can describe the same thing while remaining separate objects. Java gives you different tests for identity and content, and collections depend on the choice your class makes. Start by choosing the fields that define equality, then implement equals and hashCode as a pair. Their contracts explain why a list can find an equal object, why a hash collision is allowed, and why changing a stored key can break a lookup.
4.1 Identity and matching content
Identity asks whether two references lead to the same object. Content equality asks whether objects have the same relevant data, according to the class's rules. Two separate book objects can describe the same title and publication year without being identical.
Choose the right comparison
| Values being compared | Meaning of == | Meaning of equals |
|---|---|---|
| Primitives | Compare primitive values | No instance method on a primitive |
| References | Same object, or both null | The class's equality rule |
| Strings | Same string object | Matching character content |
Class inheriting Object.equals | Same object | Same object |
Every class inherits equals(Object) from Object. That default checks identity. Java does not inspect all the fields of a new class automatically.
String a = "java";
String b = new String("java");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
new String creates a separate object, while String.equals compares its characters. Text comparison is case-sensitive: "Java".equals("java") is false. equalsIgnoreCase gives a case-insensitive comparison when that is the intended rule.
Why identity sometimes seems to compare content
Equal string literals share a string object, so "java" == "java" is true. This can hide a bug in a small test. Text obtained from input or constructed while a program runs need not share that object. Use equals for content regardless of how the text was obtained.
Wrapper comparisons have a similar trap:
Integer a = 127, b = 127;
Integer c = 1000, d = 1000;
System.out.println(a == b); // true
System.out.println(c.equals(d)); // true
Autoboxing reuses Integer objects for values from -128 through 127. Whether larger values share an object is not a portable numeric comparison rule. On a standard setup the two 1000 wrappers are separate, but a larger cache is permitted. Use wrapper equals for matching wrapper values or compare primitives. If one side of == is an int, Java unboxes the wrapper and compares numeric values; unboxing a null wrapper fails.
Decide which fields define equality
For a book catalog, title and publication year might define equality while the current borrower is ignored. Those selected fields must drive the equality method. Another program could choose a unique identifier instead; the useful rule depends on what its objects represent.
An ArrayList uses equals in contains, indexOf and remove(Object). Suppose Book only stores fields and inherits equals. Adding one new Book("Dune", 1965) does not make contains(new Book("Dune", 1965)) true: the second expression creates a different object. Overriding equality by title and year lets a search find that equal content.
HashSet and HashMap also use hashCode to narrow the candidate objects before checking equality. Defining content equality therefore requires a matching hash-code rule as well.
Key Takeaways
- For references,
==tests identity, whileequalsfollows the class's rule. Object.equalscompares identity until overridden.- Use
String.equalsfor text content. - Shared literals and wrapper caching can make identity tests misleading.
- Choose equality fields deliberately; collections depend on that choice.
4.2 Implementing equals correctly
An equality method must replace the inherited equals(Object) and compare the fields that define your object's meaning. The correct signature matters as much as the comparisons inside it.
A complete equality pattern
This class uses title and year, allows a null title, and keeps its equality fields fixed:
import java.util.Objects;
final class Book {
private final String title;
private final int year;
Book(String title, int year) {
this.title = title;
this.year = year;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Book)) {
return false;
}
Book book = (Book) other;
return year == book.year
&& Objects.equals(title, book.title);
}
@Override
public int hashCode() {
return Objects.hash(title, year);
}
}
Read equals in four steps. The identity check accepts the same object immediately. The type check rejects anything that is not a book, including null. The cast gives a Book variable through which the fields can be read. The final expression checks both equality fields.
The parameter type is Object, not Book. Writing equals(Book other) overloads the name with another method and leaves the inherited equals(Object) unchanged. A direct call can then appear to work while ArrayList.contains still uses identity. @Override makes the compiler catch that signature mistake.
From Java 16, the type check and cast can be combined: if (!(other instanceof Book book)) { return false; }. After the check succeeds, book is available for field comparisons.
Match comparisons to field types
Use == for integral, character and boolean fields. Use an object's equals for its content. For a nullable object field, Objects.equals(a, b) returns true when both are null, false when only one is null, and otherwise uses equality.
For double fields, Double.compare(x, other.x) == 0 supplies consistent special-value handling: two NaNs compare as equal, and positive and negative zero are distinguished. Pair that choice with a compatible hash rule. Raw floating-point == treats NaN differently.
Check the equality contract
For non-null references, equality must be:
- Reflexive:
x.equals(x)is true. - Symmetric:
x.equals(y)andy.equals(x)agree. - Transitive: if
xequalsyandyequalsz,xequalsz. - Consistent: repeated calls agree while the compared data stays unchanged.
- False for null:
x.equals(null)is false.
Two books for "Dune", 1965 compare equal; changing one year to 1984 makes them unequal. A book must not compare equal to a string containing its title: the string would reject the book, breaking symmetry.
Some implementations use other == null || getClass() != other.getClass() for the type check. It rejects different runtime classes. instanceof can accept subclasses; for the final class above, no subclass exists and the two checks agree.
Key Takeaways
- Override with
public boolean equals(Object other)and@Override. - Check identity, check type, convert the reference and compare equality fields.
- Use null-safe object comparisons when fields can be absent.
- Respect reflexivity, symmetry, transitivity, consistency and rejection of null.
equals(Book)overloads the method and does not replaceequals(Object).- Type-check choices matter when subclasses are allowed.
4.3 Matching hash codes and stable keys
A hash code is an integer used to narrow a search. HashSet and HashMap use it to find candidate objects, then use equals to confirm a match. It is not a unique object identifier.
The contract goes in one direction
Equal objects must return the same hash code. Unequal objects may also share a code; that is a collision. A collision does not make them equal, because equality is still checked.
A hash code must stay consistent while the information used by equals stays unchanged. For a correct implementation, different hash codes rule out equality, but equal hash codes do not establish it.
When a class overrides equals but inherits Object.hashCode, separate equal objects commonly receive different codes. A set may then retain both, and a map lookup using a fresh equal key may miss. The exact failure is not guaranteed, because different objects can have colliding inherited codes. Fix the contract instead of relying on a particular failed lookup.
Compute from the equality fields
For the book class that compares title and year:
@Override
public int hashCode() {
return Objects.hash(title, year);
}
Import java.util.Objects. The same field values now produce the same result. A book set stores one entry after adding two equal books, and a map can retrieve a value using a fresh equal book key.
Objects.hash combines the field hashes using 31. For "Dune", the string hash is 2141736. Combining that title and year 1965 gives 31 multiplied by the sum of 31 and 2141736, then adds 1965: the result is 66396742.
String hashing also uses 31. For "ab", character codes 97 and 98 give 97 multiplied by 31 plus 98, or 3105. Longer calculations can overflow the int range and wrap, so a negative hash code is normal.
| Hash rule | Contract consequence |
|---|---|
| Use the same equality fields | Equal objects get matching codes |
| Add a field that equality ignores | Equal objects can get different codes |
| Use fewer equality fields | Legal, but may create more collisions |
| Always return 1 | Legal, but gives poor search performance |
Do not change the key during storage
Suppose a book's year is mutable and affects its hash code. After insertion in a set, changing the year can make contains(book) fail even with that same reference: the lookup now searches using a different code. The set can still report size 1.
Keep equality fields stable while an object is a set element or a map key. If they must change, remove the object while its old values still apply, change it and insert it again. Trying removal only after the change can encounter the same lookup problem.
Generated equality still needs a sound data choice
Java 16 and later support records, such as record Book(String title, int year) {}. A record generates accessors title() and year(), plus equality, hashing and a readable string form from all its components. Component fields are final. This example is a useful immutable value because its components are a string and an integer; a record holding a mutable list would still share that list's state.
For ordinary classes, an IDE can generate equals and hashCode together. Select the fields that define the intended equality and keep both methods aligned when that choice changes.
Key Takeaways
- Equal objects require equal hash codes; collisions between unequal objects are allowed.
- Override
hashCodewhenever you overrideequals. - Hash only data compatible with the equality rule.
- Keep hash-relevant fields stable during set membership or use as a map key.
- Remove before changing a key, then reinsert.
- Records generate equality from their components; mutable components still need care.
5Give each class a clear job
A text program has two kinds of work: talking to a user and managing the data and rules. Keeping those jobs in separate classes makes the rules easier to check and reuse. Connect the classes through constructor parameters and small public methods. Then add a feature first where its rule belongs, verify the result with known values, and give the interface a command that calls it and displays the answer.
5.1 Input, output and program rules
The user interface reads input and displays output. The program logic stores data and applies rules. A separated design passes values into the logic and gets results back; its logic classes do not use Scanner or System.out.
Connect the objects in main
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
TemperatureLog log = new TemperatureLog();
UserInterface ui = new UserInterface(scanner, log);
ui.start();
}
With import java.util.Scanner;, this wiring creates one input reader, one log and one interface. It belongs inside the program's entry class. UserInterface stores the supplied objects in fields so that every command works with that same log.
| Work | Class responsible |
|---|---|
| Read command text | User interface |
| Store a reading | Temperature log |
| Calculate average or maximum | Temperature log |
| Round and print a result | User interface |
| Reject an impossible temperature | Temperature log |
| Explain invalid input to the user | User interface |
Return values that any caller can use
A log might provide add(double celsius), count(), average() and max(). Readings of 18.5, 21.0 and 19.5 °C have sum 59.0, count 3, maximum 21.0 and average about 19.6667 °C. The UI can round the returned average with Math.round(average * 10) / 10.0 and show 19.7 °C.
Returning the number makes direct checks easy. A method named printAverage() that does only printing couples a calculation to console output, which makes reuse more awkward.
For a simple command loop, suppose add accepts valid temperatures and count returns their number:
class UserInterface {
private Scanner scanner;
private TemperatureLog log;
UserInterface(Scanner scanner, TemperatureLog log) {
this.scanner = scanner;
this.log = log;
}
void start() {
while (true) {
String command = scanner.nextLine();
if ("quit".equals(command)) {
break;
}
if ("count".equals(command)) {
System.out.println(log.count());
}
}
}
}
The loop handles interaction. It asks the log for a result and prints it without knowing how readings are stored. An average command should handle an empty log according to the logic's documented result or check count() before calling a method that requires readings.
Validate at the right boundary
The logic can make add return false without storing a reading below -273.15 °C. Every caller then gets the same data rule. The UI decides how to report that rejection and ask again. Converting nonnumeric input into a useful error message is also interface work.
Use one Scanner for System.in and pass it to readers. Multiple scanners can compete for buffered input, and closing one closes the underlying input stream. Keep the lifetime of the shared scanner with the code that owns the program's input.
The logic can later support another interface through the same public methods. Reuse comes from keeping calculations and validity rules available as method results, independently of a particular command loop.
Key Takeaways
- The UI reads and displays; the logic stores data, enforces rules and returns results.
mainconstructs the shared objects and starts the interface.- Pass one scanner and the logic object into the UI constructor.
- Data rules belong in the logic; input messages and display formatting belong in the UI.
- Returning results makes direct checks and interface reuse easier.
5.2 Add a feature and check its behavior
A separated program can grow one small change at a time. Give the logic a useful method, check it directly, then connect a command that gathers its input and displays its result.
Add a query to the logic
A TemperatureLog storing an initialized ArrayList<Double> readings can count readings above a limit:
int countAbove(double limit) {
int count = 0;
for (double reading : readings) {
if (reading > limit) {
count++;
}
}
return count;
}
The strict comparison excludes readings exactly equal to the limit. For 18.5, 21.0, 19.5 and 23.0 °C, countAbove(20.0) returns 2. Put the comparison in the log so every caller gets the same rule.
The UI command reads a limit, calls log.countAbove(limit) and prints the count. If each command grows long, split start() into private helpers such as addReading() and showStatistics(). The helpers organize interaction; they do not move the data rules out of the log.
Check without typing commands
Create the logic object and call its methods with values whose answers you know:
TemperatureLog log = new TemperatureLog();
log.add(18.5);
log.add(21.0);
log.add(19.5);
log.add(23.0);
System.out.println(log.count() == 4);
System.out.println(log.average() == 20.5);
System.out.println(log.max() == 23.0);
System.out.println(log.countAbove(20.0) == 2);
The sum is 82.0, so the expected average is 20.5. These particular values have exact binary representations, making these direct equality checks suitable for the example. Also check an empty log according to its documented behavior, a limit matching a reading, and rejection of invalid input without changing the count. Automated test tools build on the same idea of a known input and an expected result.
Change storage only when it still supports the queries
An interface that calls only average, count and max need not know whether the log holds a list. Running sum, count and maximum fields can answer those three queries without retaining every reading.
An arbitrary countAbove(limit) query needs more information. The samples 18, 22, 23 and 20, 20, 23 have the same sum 63, count 3 and maximum 23, but their counts above 20 are 2 and 1. Keeping only those three aggregates loses the distinction. The public methods describe what must still work after a storage change.
Combine the design ideas
A recipe book can own a private list of recipes. Each recipe can contain ingredients measured in grams and a cooking time in minutes. Equality can support duplicate checks, while a UI adds, lists and searches recipes through the book's methods. A copied result list can protect membership while still sharing recipe objects; decide whether callers may change those recipes.
Work in small steps: implement one method, check it and then add its command. If a change breaks an existing result, return to a working state and isolate that change before adding another feature.
Key Takeaways
- Add and check the logic method before connecting its UI command.
- Use known inputs and expected results to check logic directly.
- Include boundary cases such as an empty collection or an exact threshold.
- A storage change must preserve enough information for every public query.
Java Objects and References examples
1Trace variables without losing the objects
15 steps
- 1IntroductionDraw variables and objects
A variable can hold a number directly or hold a reference leading to an object. That difference explains why copying a variable sometimes gives independent data and sometimes shares a change.
We will draw variables separately from objects, then follow each statement. Keep the number of arrows separate from the number of objects: several arrows can end at one object.
- 2ExplanationA value can be data or a reference
Java has eight primitive types:
byte,short,int,long,float,double,charandboolean. Their variables hold data directly. Classes and arrays are reference types; their variables hold references ornull.In
int count = 13;, the value is 13. InTally t = new Tally(13);, the value intis a reference to an object holding its own data. - 3ExampleCopy a primitive value
Follow the assignments in order:
int start = 13; int next = start; next += 2;The second statement copies 13 into
next. The third reads that copy and replaces it with 15.startremains 13 because no statement assigns a new value to it. Draw two boxes containing separate numbers; no shared object is involved. - 4ExampleCopy a reference value
Suppose
Tallystores an integer,addOne()increases it andvalue()reads it.Tally first = new Tally(13); Tally next = first; next.addOne();One
newcreates one tally. The assignment givesnextanother route to it. CallingaddOne()changes its stored value to 14, whichfirst.value()now reads too. The second variable is not a second tally. - 5ChallengeSeparate an object from its alias
With the same
Tallymethods, what doesa.value()return?Tally a = new Tally(20); Tally b = new Tally(a.value()); b.addOne();---reveal---
It returns 20. The second
newcreates an independent tally initialized with 20; its increment changes only that second object to 21. Reading a field value and using it in a constructor differs from copying the original object reference. - 6ConnectionSome shared objects cannot be mutated
Strings and wrappers such as
Integerare reference types. Autoboxing converts primitive values to wrappers for uses such asArrayList<Integer>.Strings are immutable: if
leftandrightshare "cedar", assigningright = right.toUpperCase()does not change the original characters.leftstays "cedar". The returned uppercase result is assigned only toright. - 7AnalogyUse arrows to follow change
Think of each variable as a card showing a route to an object. Copying the card gives another route to the same destination. Writing a different route on one card does not rewrite the others.
In a memory diagram, draw an arrow from each reference variable to its object. A setter changes the object's field values; an assignment changes one variable's arrow. Keep those two edits visually distinct.
- 8ExampleChange shared state first
Let
Cratestore a mass in kilograms, withsetMassandmassmethods.Crate a = new Crate(3); Crate b = a; b.setMass(6);Draw
aandbpointing to one crate. The setter updates the mass inside that crate from 3 to 6. Botha.mass()andb.mass()now return 6; the setter has not redirected either variable. - 9ExampleThen redirect one variable
Continue the crate trace:
b = new Crate(9);Create a second object with mass 9 and redirect only
b. Variableastill points to the original crate, whose mass remains 6. There are now two reachable crate objects. This is a reassignment, so nothing about the first crate's stored mass is reset. - 10ChallengeKeep the remaining route
Continue with
Crate c = a; a = null;. Through which variable can the 6 kg crate still be reached, and is it eligible for collection?---reveal---
It is still reachable through
c, so it is not eligible for collection. The earlier assignment copied the reference intoc; nullingaremoved only one route. The separate 9 kg crate remains reachable throughb. - 11Key InsightA field or list can preserve a route
When the last usable path to an object disappears, it becomes unreachable and eligible for garbage collection. Java chooses the collection time; losing a reference does not promise immediate reclamation.
A path may run through another object or list. Before calling an object unreachable, check those links as well as local variables. A list entry can preserve an object after its original variable has gone out of scope.
- 12ExplanationNull means the destination is missing
nullis not an empty object. A nullStringhas no object on which to calllength(), while""is a usable string of length zero. Reference fields default to null; local variables need an assignment before reading.Wrappers can also be null.
Integer n = null; int count = n;throwsNullPointerExceptionwhen unboxing tries to obtain a primitive from the missing wrapper. - 13TipRead the error at the failing call
For a null error, locate the line number and then the instance call, field access or unboxing on that line. Helpful messages, enabled by default from Java 15, can identify the missing field.
Trace backward to the constructor, search result or assignment that should have supplied an object. Seeing "null" in printed text is a clue: printing a null string can succeed even though a later instance call fails.
- 14ChallengeGuard the combined trace
A search assigns
Crate found = findCrate();, returning either a crate or null. What condition safely checks for mass above 5 kg? Why must its tests be in that order?---reveal---
Use
found != null && found.mass() > 5. The null test runs first, and&&skipsmass()when no crate exists. Putting the method call first would dereference null before reaching the check. A returned reference follows the same rules as the aliases traced earlier. - 15RecapCheck a reference trace
For a fresh trace, mark each
new, copy the value at each assignment and update only the field targeted by a mutation. Then follow every remaining path before deciding an object is unreachable.At a failing line, identify the exact operation that needs an object. Check null before that operation, or initialize the field with the object the program actually needs, such as an empty list.
2Follow an object into and out of a method
12 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionFollow a call and its result
- 2ExplanationA parameter gets its own value
- 3ExampleMutate the shared object
- 4ExampleReassign the local route
- 5ChallengeCan a method swap caller variables?
- 6ConnectionCompare the receiver with its argument
- 7ExplanationThe return type does not promise a copy
- 8ExampleBuild an independent returned dial
- 9ChallengeFind an entry that can be updated
- 10TipA getter can reveal a hidden alias
- 11ChallengeCombine a return with a parameter call
- 12RecapInspect a method at both boundaries
3Build and protect a collection of objects
16 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionSeparate the layers of state
- 2ExplanationModel a has-a relationship
- 3ExampleDelegate the date rule
- 4ChallengeProtect the incoming date
- 5ExplanationAn entry is another route
- 6ExampleCompute a total through a loop
- 7ExampleKeep the selected object
- 8ChallengeReturn absence at the right time
- 9TipSeparate removal from traversal
- 10ExplanationGive the owner an initialized list
- 11ExampleOffer useful operations
- 12ChallengeDistinguish a list copy from its elements
- 13ConnectionProtect both directions
- 14ExampleFilter into a new result
- 15ChallengeFollow a copy through another object
- 16RecapReview the collection boundaries
4Build equality that collections can trust
16 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionDecide what a match means
- 2ExampleEqual text in separate objects
- 3TipDo not let caching choose the rule
- 4ChallengeGive a class content equality
- 5ExplanationChoose fields and the signature
- 6ExampleAccept identity and reject wrong types
- 7ExampleCompare the selected fields
- 8ChallengeCatch a broken contract
- 9Key InsightCheck more than one matching pair
- 10ExplanationHash codes narrow the candidates
- 11ExampleGenerate the matching hash
- 12ChallengeKeep a collision separate from equality
- 13ExampleNotice what a changed key does
- 14ConnectionA simple record supplies both methods
- 15ChallengeTrace the set after generated equality
- 16RecapAudit a value class
5Add a feature without mixing input and rules
11 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionConnect rules to a conversation
- 2ExplanationName the two jobs
- 3ExampleWire one set of shared objects
- 4ExampleReturn the result to the display
- 5ChallengePut a rejection in the right class
- 6ExplanationGive the new query a logic method
- 7ExampleCheck with known inputs
- 8ChallengeEvaluate the storage proposal
- 9TipGrow the program one checked step at a time
- 10ChallengeMove to a different interface
- 11RecapReview a new feature at its boundary
Java Objects and References practice questions
Set 1: Follow values, calls and simple comparisons10 Practice Questions
Question 1 of 10
What does original hold after this code?
int original = 8;
int duplicate = original;
duplicate = 11;
Show answerHide answer
duplicate, so original still holds 8. Treating the variables as aliases would give 11, but an int assignment copies the number itself and creates no shared object.Question 2 of 10
Lamp has a boolean field initially false; turnOn() sets it to true and isOn() reads it. What do a.isOn() and b.isOn() return, in that order?
Lamp a = new Lamp();
Lamp b = a;
b.turnOn();
b = new Lamp();
Show answerHide answer
a and b. Reassigning b then points it at a fresh lamp, whose field starts false, while a keeps the lit lamp. Predicting two true results misses the new object created by the final statement.Question 3 of 10
label may be null. What condition safely checks that it has at least three characters?Show answerHide answer
&& skips the length call when that check is false. Putting the length call first already dereferences the missing object. A single & also evaluates both sides, so it does not provide the needed short-circuit protection.Question 4 of 10
Robot r. It executes only r = new Robot();. What happens to the caller's variable that was passed as the argument?Show answerHide answer
Question 5 of 10
Gauge.copy() returns new Gauge(value). A gauge starts at 6, and raise() adds 1. What values do first and second report?
Gauge first = new Gauge(6);
Gauge second = first.copy();
second.raise();
Show answerHide answer
Question 6 of 10
Device constructor stores its mutable Settings argument directly. Its getter returns a copy of the stored settings. Two devices receive the same settings object, and the caller changes that original object afterward. What design change makes each device independent of that outside change and of the other device?Show answerHide answer
Question 7 of 10
Sensor s is added to two lists. One list removes its entry for s. What happens to the sensor in the other list?Show answerHide answer
Question 8 of 10
What do these two expressions return?
String word = new String("maple");
word == "maple"
word.equals("maple")
Show answerHide answer
String.equals compares the characters and returns true. Equal text does not make separately created strings identical, even though equal literals can share an object.Question 9 of 10
Object?Show answerHide answer
equals, return type boolean and parameter type Object. Matching that declaration overrides it; adding @Override lets the compiler check the match. A parameter of type Note creates an overload and leaves the inherited method in place.Question 10 of 10
Show answerHide answer
Set 2: Apply sharing, collections and program boundaries10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
What happens to a after the last line?
String a = "river";
String b = a;
b = b.toUpperCase();
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
What happens when this code runs?
Integer reading = null;
int value = reading;
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Meter stores an integer, with set(int) and get(). What does m.get() return after the call?
static void adjust(Meter x) {
x.set(14);
x = new Meter(22);
x.set(25);
}
Meter m = new Meter(9);
adjust(m);
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Bin.items() returns its private list of mutable Part objects directly. A caller must be allowed to inspect and modify the returned list and parts without changing the bin's membership or stored part state. What must the result contain?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
return null in an else after the name test inside the loop. Where should that return go?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
copy is created with new ArrayList<>(original). Both lists initially contain the same mutable Clip c. The caller changes c through copy.get(0), then clears copy. What remains in original?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Badge has fields and a constructor but does not override equals. What is the result?
new Badge("Q7").equals(new Badge("Q7"))
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Tag.equals compares only code. Tag.hashCode uses both code and mutable color. Two tags have the same code but different colors. A developer proposes returning a constant hash code to repair correctness. How should the two rules be assessed?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
count(), average() and max(). Its developer adds countAbove(limit) for any caller-supplied limit, then replaces all readings with only a sum, count and maximum. Why can the new query no longer be implemented correctly for every log?Show answerHide answer
Save this lesson to see this answer.
Set 3: Debug aliases, equality and collection design10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Only the references shown exist, and all variables remain in use. Cell has mutable value data. How many cell objects remain reachable?
Cell a = new Cell(3);
Cell b = new Cell(8);
Cell c = a;
a = b;
b = null;
c = a;
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
private ArrayList<Note> notes;. Its constructor never assigns notes, and add immediately calls notes.add(note). What change makes a newly constructed instance ready to store notes?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Box has an integer field read by get() and changed by set(int). What values do left.get() and right.get() report after this call?
static void revise(Box x, Box y) {
x.set(10);
x = y;
x.set(16);
}
Box left = new Box(4);
Box right = new Box(7);
revise(left, right);
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Member stores a non-null SimpleDate joined and needs to decide whether it joined before another member. Where should the year, month and day comparison be implemented?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Queue constructor stores an incoming ArrayList<Job> directly, but its getter returns a new shallow list. The caller clears the original input list. The queue must keep its original membership, while job objects may remain shared. What repair meets that requirement?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Token declares public boolean equals(Token other) without @Override. Direct comparisons appear correct, but an ArrayList<Token> fails to find a fresh token with matching fields. What is the signature problem?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Key.equals and hashCode both use serial. A mutable key is added to a HashSet, then its serial changes and a lookup fails. What sequence correctly supports a planned serial change while preserving membership?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
countAbove(limit) counts readings strictly greater than its argument. A log contains 11.5, 15.0, 15.0 and 18.5 °C. What should a direct check expect for countAbove(15.0)?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Mark.equals compares code. A list holds marks with codes "A", "B", "A"; the two A marks are separate objects. A second list contains only a reference to the first A mark. What remains after marks.removeAll(second)?Show answerHide answer
Save this lesson to see this answer.
Set 4: Reason through copies, contracts and design changes10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Node contains mutable data. Only the references shown exist. At the final line, how many node objects are reachable through first or the list?
Node first = new Node();
ArrayList<Node> nodes = new ArrayList<>();
nodes.add(first);
first = new Node();
nodes.add(first);
nodes.remove(0);
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Locker.find(id) currently returns a newly constructed mutable Item with the same field values as the matching stored item. Callers use the result to update the stored item's label. What behavior must the method provide to make those updates affect the locker?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Schedule objects store one shared mutable SimpleDate field. A developer makes only the schedule fields final. What happens if the original date object is changed through its setter?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Document objects. matching(prefix) builds a fresh list containing existing matching documents. A caller removes one result and edits another result's title. What effect can this have on the library?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
x and y, what does x == y establish when it is true?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Score.equals(Object) method accepts another score when the absolute difference between their integer values is at most 2. Scores x, y and z hold 10, 12 and 14. No values change. What equality-contract problem appears across these three objects?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
record Stamp(String code, int batch) {} uses generated equality and hashing. A set receives new Stamp("R", 4), a separate new Stamp("R", 4), then new Stamp("R", 5). How many entries does it contain?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Double.compare(level, other.level) == 0 in equals. A proposed hash method always returns 1. Another returns different codes for different objects whose levels are both NaN. How do these proposals compare with the equality contract?Show answerHide answer
Save this lesson to see this answer.
Stuck on your own question? Homework Help
Key Terms
A
Aggregate
Alias
Box second = first;, second is an alias and its setter changes the box reached by first.Argument
gate.allowIn(ana), the argument is the reference value read from ana, which the parameter copies.ArrayList
ArrayList<Parcel> can hold three parcel references and let a loop add their weights in kilograms.Autoboxing
numbers.add(12) pass an Integer value when numbers is an ArrayList<Integer>.C
Collection query
songsLongerThan(200) builds a new list while retaining the matching song references.Composition
ConcurrentModificationException
ConcurrentModificationException before traversal finishes.Consistency
Constructor injection
Content equality
Copy method
new Counter(value) so increasing the copy leaves the original unchanged.D
Data validation
Default field value
ArrayList<Song> field is null, so no list exists yet.Defensive copy
Delegation
olderThan calls birthday.before(other.birthday) for its date comparison.Direct check
E
Encapsulation
Equality fields
F
Factory method
issue() constructs a ticket with its next number and returns it.Final reference field
add.For-each loop
getWeight() result without changing list membership.G
Garbage collection
H
Hash code
Objects.hash(title, year) when those fields define equality.Hash collision
equals correctly distinguishes them.HashMap key
HashSet
HashSet<Book> retains one entry for two separate equal books when their equality and hashes agree.I
Identity
== for two references; matching field values alone do not establish it.Immutability
toUpperCase() cannot change the characters in the original string.L
List membership
M
Mutable object
Mutation
parcel.setWeight(3.5) is a mutation that a list holding that parcel can also observe.N
Null reference
null, which leads to no object and can be held by a reference variable but not a primitive variable.String name cannot receive length(), while an empty string can return length 0.NullPointerException
name.length() throws NullPointerException when the name variable holds null.O
Object field
birthday in Person can lead to a SimpleDate that performs date comparisons.Overload
equals(Book other) creates an overload while leaving equals(Object) available to collections.Override
equals(Object) in a new class.@Override annotation lets the compiler check that a book's equality method is a valid override.Owner object
add method.P
Parameter
allowIn(Person visitor), the parameter visitor receives the caller's supplied person reference.Pass-by-value
Primitive type
int lets count hold 12 directly, so assigning it to another variable copies 12.Program logic
Public contract
R
Reachability
Reassignment
current = new Counter(); redirects current while other aliases keep the old counter.Receiver
this refers to that object.earlier.before(later), the receiver is earlier; the other date is supplied as the argument.Record
Book(String title, int year) compares both components and provides title() and year() accessors.Reference
Counter second = first; lets both variables reach the original counter.Reference type
null.String is a reference type, so a variable can refer to a text object or hold no object reference.Reflexivity
x.equals(x).this == other check in equals accepts reflexivity before comparing any field values.Return value
findByName can lead to a stored person that the caller and the register then share.S
Search result
Shallow copy
Short-circuit evaluation
&& when its left side is false, allowing a prior check to protect a later call.name != null && name.length() > 0 skip the method call for null.String interning
"oak" == "oak" true, hiding a bug that appears when equal text comes from input.Symmetry
T
Transitivity
U
Unboxing
NullPointerException for a null wrapper.int count = boxed;, so boxed must refer to an actual Integer object.User interface
add method and displays any rejection message.W
Wrapper cache
equals instead of relying on wrapper ==.Wrapper class
Integer supplies the element type for ArrayList<Integer>; primitive int cannot.About this lesson
Make your own Study Materials
Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.