Point, Rectangle, and Why Mutability Bites

Objects with attributes you can reach: creating Points and Rectangles, reading their data with dot notation, passing objects to methods and returning new ones, changing an object's attributes, and the aliasing that makes mutable objects genuinely hard to debug. Follows Think Java 2e, Chapter 10 (Mutable Objects), Sections 10.1-10.5, pp. 167-173, cross-referenced against Java SE 21 API — java.awt.Point.

Subject: Java · 65 slides · code lesson

Open the interactive version of this deck

What this lesson covers

The lesson, slide by slide

1. Point, Rectangle, and Why Mutability Bites

Title

Think Java 2e · Chapter 10 · Mutable Objects

Sections 10.1-10.5 · pp. 167-173

2. What you will be able to do

Objectives

This lesson follows Think Java 2e, Chapter 10 (Mutable Objects), Sections 10.1-10.5, pp. 167-173. Everything on these slides can be checked against those pages.

1. Create a Point or Rectangle with new, and access its attributes with dot notation.

2. Explain why there is no conflict between a local variable x and an attribute x.

3. Pass an object to a method, and say why bundling related values is less error-prone.

4. Write a method that returns a newly created object.

5. Modify an object's attributes directly and through a method, and predict the effect on the caller.

6. Draw the memory diagram for two variables referring to one object, and explain what aliasing makes difficult.

3. Retrieve before you read

Warm-up

Two facts from Chapter 7 are about to become much more consequential.

Discussion prompt

From Lesson 7a: what happens when you assign one array variable to another? And can a method change the contents of an array it was passed?

Hint: One array, two names.

Answer:

The assignment copies the reference, so both variables refer to the same array — they are aliases. And yes, a method can change the contents, because it receives a copy of the reference rather than a copy of the array.

Everything in this chapter is that behaviour, applied to objects rather than arrays. The difference is that Chapter 9's objects — Strings and Integers — were immutable, so aliasing them was harmless. These are not.

4. Objects whose data you can reach

Concept

Chapter 9's objects kept their data to themselves: you could call length() on a String but never look inside it. Point and Rectangle are different — their data is public, so you can read it and change it directly.

Figure (svg): A variable named blank with an arrow to a Point object containing two attributes, x holding 3 and y holding 4

Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 10 (Mutable Objects), Sections 10.1-10.5, pp. 167-173 — Chapter 10 opens on printed page 167.

5. Point objects and dot notation

Section

Section 10.1

6. An object with two attributes

Concept

The java.awt package provides a class named Point representing a location in a Cartesian plane. You import it, create one with new, and the result of new is a reference to the object.

import java.awt.Point;

Point blank;
blank = new Point(3, 4);

int x = blank.x;
System.out.println(blank.x + ", " + blank.y);      // 3, 4
int sum = blank.x * blank.x + blank.y * blank.y;   // 25
linewhat it does
Point blank;declares that blank has type Point — no object yet
blank = new Point(3, 4);creates the object and makes blank refer to it
blank.xgo to the object blank refers to, and get its x
blank.x * blank.x + blank.y * blank.yan ordinary expression using two attributes

attribute — One of the named data items that make up an object. Also called a field.

dot notation — Use of the dot operator to access an object's attributes or methods.

Variables that belong to an object are called attributes — in some documentation you will see them called fields. The expression blank.x means: go to the object blank refers to, and get the value of the attribute x.

7. Dot notation, for data as well as methods

Notation

You have used the dot for methods since Chapter 1. Now it reaches data too, and the reading is the same.

Annotate

  • **The dot always means go inside.** In Lesson 3a it took you from a class to a variable to a method; here it takes you from an object to one of its attributes.
  • No parentheses means data, not a method. blank.x is an attribute and blank.distance(p) is a method — exactly the distinction between a.length and s.length() from Lesson 7a.
  • Attributes can be read and written. blank.x on the right of an assignment reads it; on the left it changes the object, which is Section 10.4.
  • Dot notation exists to remove ambiguity. int x = blank.x; declares a local x and reads the attribute x on the same line, and there is no conflict — the dot says which one you mean.

That last point is worth dwelling on: there is no conflict between the local variable x and the attribute x, because the purpose of dot notation is to identify which variable you are referring to unambiguously.

8. Reading attributes in an expression

Worked example

An attribute behaves exactly like any other variable of its type. Anywhere an int can go, blank.x can go.

Point blank = new Point(3, 4);

System.out.println(blank.x + ", " + blank.y);
int sum = blank.x * blank.x + blank.y * blank.y;
System.out.println(sum);
expressionsubstitutingvalue
blank.x—3
blank.y—4
blank.x + ", " + blank.y3 + ", " + 4the string 3, 4
blank.x * blank.x + blank.y * blank.y9 + 1625

Read each attribute as you would read a variable.

Why: blank.x is an int with the value 3.

Note the third row.

Why: This is Chapter 2's concatenation rule — the string operand makes the whole expression a string, so the numbers are converted to text.

Note the fourth row.

Why: Pure arithmetic, because no string is involved. 3 squared plus 4 squared is 25.

Note that nothing is modified.

Why: Reading an attribute leaves the object exactly as it was.

Verify: Expect 3, 4 and then 25.

Why: The 25 is worth recognising: it is the square of the distance from the origin to (3, 4), which is 5 — the 3-4-5 triangle from Lesson 4b, appearing again.

9. What does this display?

Prediction

Reading two attributes.

Point p = new Point(5, 12);
System.out.println(p.x + p.y);
expressionvalue
p.x5
p.y12
p.x + p.y17 — both are ints, so this is addition

Predict first

What is displayed?

  • 17
  • 512
  • 5, 12
  • 5 12

Correct: 17

Why: Both attributes are ints, so + performs addition rather than concatenation — Chapter 2's rule that the operand types decide what + means. To display them separately you would need a string in the expression, as in p.x + ", " + p.y.

10. Why attributes are drawn inside the object

Concept

The memory diagram has a specific shape, and every part of it means something you have seen before.

Figure (svg): A variable box labelled blank with an arrow to a Point object whose interior holds two labelled attribute boxes

part of the diagrammeans
the name outside the boxthe variable's name — as in Chapter 2
the value inside the boxwhat the variable holds
the arrowthe value is a reference
the two boxes at the end of itthe object's attributes

Compare this with a primitive from Lesson 9a: int number = -2; is one box with a value in it. The only difference here is that the value is a reference, so there is something at the other end — and that something has boxes of its own.

11. Declaring a Point without creating one

Trap

The trap

The variable exists; the object does not.

Point blank;
System.out.println(blank.x);      // no object to look inside
statementwhat existsresult
Point blank;a variable that could refer to a Pointno object
blank.xnothing to follow the reference tocompile error: variable blank might not have been initialized

This is Lesson 7a's uninitialised-array error again. And if the variable had been explicitly set to null, it would compile and throw a NullPointerException instead — Lesson 9a's exception, now reached through an attribute rather than a method.

The fix

Declare and create, ideally in one statement.

Point blank = new Point(3, 4);
System.out.println(blank.x);      // 3
what new doesresult
allocates memory for the objectthe Point exists
runs its constructor with (3, 4)x is 3 and y is 4 — Chapter 11 explains constructors
returns a referencewhich the assignment stores in blank

The two-line form is legal and occasionally necessary, exactly as in Lesson 2a — but until new runs, there is no object, and the dot has nothing to look inside.

12. Attribute or method?

Definition probe

The parentheses tell you.

Sort into buckets

Sort each expression.

an attribute — data
blank.x; box.width
a method — behaviour
blank.distance(other); box.translate(5, 5); s.length()
attr
No parentheses. It names a piece of the object's data, and it can be read or assigned like any variable.
meth
Parentheses, with or without arguments. It invokes behaviour rather than naming data — the same distinction as a.length against s.length().

13. Create a point and read it

Fill the middle

Import, create, access.

Fill in the blanks

import java.awt.Point;

Point p = new Point(3, 4);
int a = p.x;

Why: new allocates the object and returns a reference to it; without it you would have a variable referring to nothing. The dot then reaches inside that object for the attribute named x — reading as go to the object p refers to, and get its x.

14. Why is there no conflict between x and blank.x?

Socratic

The same name in the same statement, meaning two different things.

Discussion prompt

int x = blank.x; declares a local variable called x and reads an attribute called x. Why does the compiler not object, and what would you have to write to get a genuine conflict?

Hint: How many characters distinguish the two names?

Answer:

They are different names: one is x and the other is blank.x. The dot is part of the expression, so there is nothing ambiguous to resolve.

That is exactly what dot notation is for — Think Java says its purpose is to identify which variable you are referring to unambiguously. It is the same reason main's hour and printTime's hour coexisted in Lesson 4a: the compiler always has a way to tell which one you mean.

A genuine conflict would need two things with the same full name in the same scope — two local variables called x, say. That is a compile error, and it is a different situation entirely.

15. Objects as parameters

Section

Section 10.2

16. Pass an object the usual way

Concept

You can pass objects as parameters exactly as you pass anything else. The method receives a reference, and can read the object's attributes through it.

public static void printPoint(Point p) {
    System.out.println("(" + p.x + ", " + p.y + ")");
}

// printPoint(blank) displays (3, 4)
stepwhat happens
printPoint(blank)the argument is evaluated — a reference
parameter passingp is assigned a copy of that reference
p.xfollows the reference to the same object blank refers to
the method returnsp disappears; the Point does not

This is Lesson 4a's parameter passing, unchanged: the parameter gets a copy of the argument's value, and the value of an object variable is a reference. So p and blank refer to the same Point.

17. Two variables, one object

Picture it

While the method runs there are two references to the same Point — one in the caller and one in the method.

Figure (svg): Two variables named blank and p each with an arrow to the same Point object holding x 3 and y 4

Nothing is copied except the arrow. That is why passing a large object to a method is cheap — and, as Section 10.4 will show, why the method can change the object the caller is holding.

18. Rewriting distance to take Points

Worked example

Lesson 4b's distance took four doubles. Taking two Points instead bundles the related values together.

// before: four separate parameters
public static double distance
        (double x1, double y1, double x2, double y2) { ... }

// after: two objects
public static double distance(Point p1, Point p2) {
    int dx = p2.x - p1.x;
    int dy = p2.y - p1.y;
    return Math.sqrt(dx * dx + dy * dy);
}
four doublestwo Points
parameters42
can you swap x1 and y1 by mistake?yes, easilyno — they belong to a Point
does the call say what it means?distance(1, 2, 4, 6)distance(p1, p2)
related values kept together?noyes

Replace the four coordinates with two objects.

Why: Each Point already carries an x and a y that belong together.

Read the attributes inside the method.

Why: p2.x - p1.x — the same arithmetic as before, with the values reached through references.

Note what became impossible.

Why: You can no longer pass the arguments in the wrong order within a point, because a Point's x and y travel as a unit.

Note the call site.

Why: distance(p1, p2) says what it means; distance(1.0, 2.0, 4.0, 6.0) requires the reader to know the convention.

Verify: Call it with (0, 0) and (3, 4) and expect 5.0 — the 3-4-5 triangle again.

Why: Passing objects as parameters makes the source code more readable and less error-prone because related values are bundled together. That sentence is the whole argument for objects as a design idea, stated early.

19. What does main print?

Prediction

The method modifies an attribute.

public static void bump(Point p) {
    p.x = p.x + 10;
}

Point q = new Point(1, 2);
bump(q);
System.out.println(q.x);
stepq.x
new Point(1, 2)1
bump(q) — p refers to the same object1
p.x = p.x + 1011

Predict first

What is displayed?

  • 11
  • 1
  • 10
  • a compile error

Correct: 11

Why: The parameter receives a copy of the reference, so p and q refer to the same Point — and changing an attribute changes that one object, which the caller can see. Compare this with the int example from Lesson 4a, where bump(x) left the caller's x alone: the difference is entirely that a Point variable holds a reference.

20. You do not need either method

Concept

Both printPoint and distance already exist, which is worth knowing before you write them.

Point p1 = new Point(0, 0);
Point p2 = new Point(3, 4);

double dist = p1.distance(p2);        // 5.0

System.out.println(p1);
// java.awt.Point[x=0,y=0]
you might writealready exists as
distance(p1, p2)p1.distance(p2)
printPoint(p)System.out.println(p)

Point objects provide a method called toString that returns a string representation. When you call println with an object, it automatically calls toString and displays the result — which is why printing a Point gives java.awt.Point[x=3,y=4] rather than the type@address you saw for arrays in Lesson 7a. Chapter 11 shows how to write toString for your own classes.

21. Assuming the object is copied

Trap

The trap

The method appears to get its own Point. It does not.

public static void shift(Point p) {
    p.x = p.x + 100;
}

Point blank = new Point(3, 4);
shift(blank);
System.out.println(blank.x);      // predicted 3
stepblank.xwhy
before the call3—
p receives a copy of the reference3one object, two references
p.x = p.x + 100103the object was changed through p
after the return103the change persists

The output is 103. Lesson 4a said a method cannot change its caller's variables — and that is still true. blank still refers to the same object. What changed is the object itself.

The fix

Distinguish changing the variable from changing the object.

public static void reassign(Point p) {
    p = new Point(99, 99);        // changes only p
}

public static void mutate(Point p) {
    p.x = 99;                     // changes the OBJECT
}
the method doesthe caller sees
p = new Point(...)nothing — only the method's own reference moved
p.x = 99the change, because it is the same object

This is exactly Lesson 7a's boundary question about arrays: assigning to p moves the arrow; assigning to p.x changes what the arrow points at. The first is local to the method; the second is not.

22. Does the caller see the change?

Definition probe

Assigning to the parameter, or to something the parameter points at?

Sort into buckets

Sort each statement inside a method taking Point p.

the caller sees it
p.x = 99;; p.y = p.y + 1;
local to the method
p = new Point(99, 99);; p = null;
seen
The object is modified through the reference, and the caller refers to that same object. This is what mutability makes possible.
local
Only the method's own parameter variable is reassigned. The caller's variable still refers to the original object, which is unchanged.

23. Four parameters or two objects

Comparison

Fill the blanks from the two versions of distance.

Comparison matrix

four doublestwo Points
the call readsdistance(1.0, 2.0, 4.0, 6.0)distance(p1, p2)
can coordinates be mixed up?yes — four numbers in a rowno — each x travels with its y
what the method receivesfour valuestwo references

Bundling related values into an object is the first real argument for object-oriented design in this book, and it is a readability argument before it is anything else.

24. Why does println show a Point's contents?

Explain it to yourself

Arrays printed as an address. Points do not.

Discussion prompt

In Lesson 7a, printing an array gave [I@bf3f7e0. Printing a Point gives java.awt.Point[x=3,y=4]. What must be different about the Point class?

Hint: What method does println call on an object?

Answer:

Point provides a toString method that returns a readable string representation, and println automatically calls it. Arrays do not have one, so they fall back on the default — the type and the address.

That is also what Arrays.toString was doing in Lesson 7a: supplying the readable form that arrays lack. And it is why String prints its characters while most objects print an address.

Chapter 11 shows how to write toString for your own classes, which turns out to be one of the most immediately useful things you can add to one.

25. Objects as return values

Section

Section 10.3

26. A method can create and return an object

Concept

java.awt also provides a class named Rectangle. Rectangles are similar to points but have four attributes: x, y, width and height — and a method can build one object and return another.

import java.awt.Rectangle;

Rectangle box = new Rectangle(0, 0, 100, 200);
System.out.println(box);
// java.awt.Rectangle[x=0,y=0,width=100,height=200]
attributevaluemeaning
x0left edge
y0top edge
width100how wide
height200how tall

Again println uses the toString method provided by Rectangle, which knows how to represent Rectangle objects as strings. Four attributes rather than two, and everything else is the same.

27. A Rectangle in memory

Picture it

The same picture as a Point, with two more boxes inside.

Figure (svg): A variable named box with an arrow to a Rectangle object holding four attributes x, y, width and height

Nothing about the diagram is new. The number of attributes an object has does not change how it is drawn or how it behaves — which is exactly the point of having a consistent model.

28. Returning a newly created object

Worked example

findCenter takes a Rectangle and returns a Point — a different type from the one it was given.

public static Point findCenter(Rectangle box) {
    int x = box.x + box.width / 2;
    int y = box.y + box.height / 2;
    return new Point(x, y);
}
stepexpressionvalue for (0, 0, 100, 200)
xbox.x + box.width / 20 + 50 = 50
ybox.y + box.height / 20 + 100 = 100
returnnew Point(x, y)a reference to a new Point(50, 100)

Declare the return type as the type you will return.

Why: public static Point findCenter(...) — Point, not void and not Rectangle.

Read the input object's attributes.

Why: Four reads, combined arithmetically. Note that / 2 is integer division, so odd widths lose half a unit.

Create the new object.

Why: new Point(x, y) allocates it and produces a reference.

Return the reference.

Why: The last line creates a new Point object and returns a reference to it — the object outlives the method, exactly as randomArray's array did in Lesson 7b.

Verify: Call findCenter(new Rectangle(0, 0, 100, 200)) and print the result: java.awt.Point[x=50,y=100].

Why: Then try a rectangle of width 101 and notice the centre comes out at 50 rather than 50.5 — integer division from Lesson 2a, quietly present in a method that looks like geometry.

29. What does findCenter return?

Prediction

Read the attributes and do the arithmetic.

public static Point findCenter(Rectangle box) {
    int x = box.x + box.width / 2;
    int y = box.y + box.height / 2;
    return new Point(x, y);
}
expressionfor (10, 20, 40, 60)value
box.x + box.width / 210 + 2030
box.y + box.height / 220 + 3050

Predict first

For new Rectangle(10, 20, 40, 60), what Point comes back?

  • (30, 50)
  • (25, 40)
  • (50, 80)
  • (20, 30)

Correct: (30, 50)

Why: Half the width is 20 and half the height is 30, added to the corner at (10, 20), giving (30, 50). Note the order of operations from Lesson 2b: division binds tighter than addition, so box.x + box.width / 2 halves the width first rather than adding then halving.

30. Objects in, objects out

Concept

With objects as both parameters and return values, methods can be composed the way Math methods were in Lesson 4b.

Rectangle box = new Rectangle(0, 0, 100, 200);
Point centre = findCenter(box);
double d = centre.distance(new Point(0, 0));
steptype produced
new Rectangle(...)Rectangle
findCenter(box)Point
centre.distance(...)double

Each step's output is the next step's input, and the types have to line up — which is exactly the composition rule from Lesson 4b. A method call that returns an object is an expression, so it can go anywhere an object of that type can.

31. Returning a reference to a local variable's object

Trap

The trap

A worry that turns out not to apply — but it is worth understanding why.

public static Point findCenter(Rectangle box) {
    int x = box.x + box.width / 2;
    int y = box.y + box.height / 2;
    return new Point(x, y);
}
// x and y disappear when the method returns.
// Does the Point disappear too?
when the method returnswhat happens
the local variables x and ydisappear with the frame
the Point objectsurvives — the caller has a reference to it

The frame disappears, taking the local variables with it. The object does not, because it was allocated separately and something still refers to it. This is the same situation as randomArray in Lesson 7b.

The fix

Objects outlive the method that created them, as long as a reference survives.

Point centre = findCenter(box);
// findCenter's frame is gone.
// centre still refers to the Point it created.
what disappears at returnwhat survives
the method's framethe object it allocated
its parameters and local variablesany object a returned reference points to

The rule to hold: frames and objects have separate lifetimes. A frame lives from call to return; an object lives as long as something refers to it — which is precisely what Section 10.9's garbage collection is about.

32. Return a new object

Fill the middle

A method that builds and returns a Point.

Fill in the blanks

public static Point origin() new} Point(0, 0);
}

Why: The return type is the type of object the method promises to produce — Point, exactly as int was for factorial in Lesson 4b. new allocates the object, and what is returned is a reference to it, so the object survives the method's frame disappearing.

33. What type does each expression produce?

Definition probe

Composition only works when the types line up.

Sort into buckets

Sort each expression by its type.

a reference to an object
new Rectangle(0, 0, 10, 20); findCenter(box); new Point(3, 4)
a primitive value
box.width; p1.distance(p2)
obj
Either new created an object, or a method returned a reference to one. The value is a reference, and it can be assigned to a variable of that class type.
prim
An attribute of primitive type, or a method returning one. width is an int and distance returns a double.

34. What if the width is odd?

Edge cases

The centre calculation uses integer division.

Discussion prompt

findCenter computes box.x + box.width / 2 with int attributes. What happens for a rectangle of width 101, and is that a bug?

Hint: Lesson 2a.

Answer:

101 / 2 is 50, not 50.5, because both operands are ints and integer division rounds toward zero. So the reported centre is half a unit to the left of the true one.

Whether that is a bug depends on what the method is for. For placing something on a pixel grid it is exactly right — there is no such thing as half a pixel. For a geometric calculation it is a real loss of precision.

The point is that it is a decision, and the code does not say which one was made. Returning a Point forces integer coordinates; if fractional centres mattered, the method would have to return something else. That is a constraint the type imposed, quietly.

35. Rectangles are mutable

Section

Section 10.4

36. Change the contents by assigning to an attribute

Concept

You can change the contents of an object by making an assignment to one of its attributes. To move a rectangle without changing its size, modify its x and y values.

Rectangle box = new Rectangle(0, 0, 100, 200);
box.x = box.x + 50;
box.y = box.y + 100;
// now at (50, 100, 100, 200)
statementxywidthheight
new Rectangle(0, 0, 100, 200)00100200
box.x = box.x + 50;500100200
box.y = box.y + 100;50100100200

mutable — An object that can be modified at any time. Points and rectangles are mutable by design.

An attribute on the left of an assignment writes into the object. This is the first time in the book you have been able to change an object — Strings and Integers in Chapter 9 refused.

37. The object after two assignments

Picture it

One object, changed in place. No new Rectangle was created.

Figure (svg): A Rectangle object whose x and y attributes now hold 50 and 100 while width and height are unchanged

Compare this with Lesson 9a's name = name.toUpperCase(), where a second object was created and the variable was pointed at it. Here there is still exactly one object, and it is different from what it was.

38. Encapsulating and generalizing the move

Worked example

Lesson 9b's process, applied here: wrap the working lines in a method, then replace the literals with parameters.

public static void moveRect(Rectangle box, int dx, int dy) {
    box.x = box.x + dx;
    box.y = box.y + dy;
}
callbox beforebox after
moveRect(box, 50, 100)(0, 0, 100, 200)(50, 100, 100, 200)
moveRect(box, -10, 0)(50, 100, 100, 200)(40, 100, 100, 200)

Wrap the two assignments in a method.

Why: Encapsulation, from Lesson 9b — the lines are unchanged.

Replace the literals with parameters.

Why: dx and dy indicate how far to move the rectangle in each direction.

Note the return type.

Why: void — the method changes the object it was given rather than producing a new one.

Note what the caller sees.

Why: Invoking this method has the effect of modifying the Rectangle that is passed as an argument.

Verify: Call moveRect(box, 50, 100) and print box: java.awt.Rectangle[x=50,y=100,width=100,height=200].

Why: The method returned nothing and yet something changed. That is worth pausing on — modifying objects by passing them as arguments can be useful, but it can also make debugging difficult, because it is not always clear which method invocations modify their arguments.

39. What is box after this?

Prediction

A void method that takes an object.

Rectangle box = new Rectangle(0, 0, 100, 200);
moveRect(box, 50, 100);
System.out.println(box.x);
stepbox.x
new Rectangle(0, ...)0
moveRect modifies the object50

Predict first

What is displayed?

  • 50
  • 0
  • 100
  • a compile error

Correct: 50

Why: The method receives a copy of the reference, so it and the caller refer to the same Rectangle — and assigning to box.x inside the method changes that one object. The method returns nothing and yet the caller's rectangle has moved, which is exactly what makes mutable objects convenient and hard to debug.

40. The object-oriented style

Concept

Java provides methods that do this for you — and the way you invoke them is the more idiomatic style.

// passing the object as an argument
moveRect(box, 50, 100);

// invoking a method ON the object
box.translate(50, 100);
moveRect(box, dx, dy)box.translate(dx, dy)
who owns the behavioursome other classRectangle itself
the object isan argumentthe thing the method runs on
reads asmove this rectanglerectangle, translate yourself
effectidenticalidentical

translate has the same effect as moveRect, but instead of passing the rectangle as an argument you use dot notation. This syntax — using dot notation to invoke a method on an object, rather than passing it as a parameter — is more consistent with the style of object-oriented programming, and Chapter 11 is about writing classes that work this way.

41. Not knowing which methods modify their arguments

Trap

The trap

Two methods, same signature shape, opposite behaviour.

// modifies the argument, returns nothing
public static void moveRect(Rectangle box, int dx, int dy) { ... }

// returns a new object, modifies nothing
public static Point findCenter(Rectangle box) { ... }

// which does this one do?
someMethod(box);
cluewhat it suggests
returns voidit probably modifies something — otherwise it did nothing
returns an objectit probably creates rather than modifies
neither is guaranteedyou have to read the method or its documentation

There is no rule in the language that enforces this. It is not always clear which method invocations modify their arguments, and that is the central difficulty of working with mutable objects.

The fix

Use the return type as a signal, and say so in the name.

void moveRect(Rectangle box, int dx, int dy)     // modifies
Point findCenter(Rectangle box)                 // creates
void translate(int dx, int dy)                  // modifies (on the object)
conventionmeaning
a void method taking an objectalmost certainly modifies it
a method returning a new objectalmost certainly does not modify its input
a verb name like move, set, addsuggests modification
a noun-ish name like findCenter, getXsuggests no modification

These are conventions rather than rules, and they are why naming matters so much for mutable objects. Chapter 9's immutable objects avoided the question entirely — a String method cannot modify anything, so you never have to wonder.

42. Does this modify the object?

Discrimination

Reading an attribute, or writing one?

Sort into buckets

Sort each statement.

modifies the object
box.width = 50;; box.translate(5, 5);
only reads it
int w = box.width;; Point c = findCenter(box);; System.out.println(box);
mod
An attribute is assigned, either directly or by a method that does so. The object is different afterwards.
read
The object's data is read and used, but nothing inside it changes. Creating a new object from it does not count as modifying it.

43. Mutable against immutable, so far

Comparison

Fill the blanks from Chapters 9 and 10.

Comparison matrix

String (Chapter 9)Rectangle (Chapter 10)
can you assign to its data?no — no public attributesyes — box.x = 50
what a method returnsa new objectoften void, having modified this one
two variables referring to itcompletely safea hazard — changes are shared

The bottom row is the subject of the next idea, and it is why Chapter 9 spent a whole chapter on immutability before this chapter introduced its opposite.

44. Why is void a clue?

Explain it to yourself

A method's return type says something about what it does.

Discussion prompt

A method that takes a Rectangle and returns void has to do something observable, or calling it would be pointless. What are its options, and what does that tell you before you read the body?

Hint: What can a method affect, if it returns nothing?

Answer:

It can print something, or it can modify an object it was given. Those are essentially the only ways a void method can matter — it cannot hand anything back.

So a void method taking an object is a strong hint that it mutates that object. moveRect and translate both fit; so do Arrays.sort and Collections.shuffle, which you will meet in Chapter 13.

The converse is a useful habit too: if you write a void method that neither prints nor modifies anything, it does nothing at all — which is exactly the ignored-return-value bug from Lesson 4b, seen from the other side.

45. Aliasing revisited

Section

Section 10.5

46. One object, several names

Concept

When you assign an object to a variable, you are assigning a reference. It is possible to have multiple variables referring to the same object — and now that objects are mutable, that matters.

Rectangle box1 = new Rectangle(0, 0, 100, 200);
Rectangle box2 = box1;

box1.grow(50, 50);

System.out.println(box1);
System.out.println(box2);
// both print x=-50, y=-50, width=200, height=300
statementobjectsbox1box2
new Rectangle(0, 0, 100, 200)1(0,0,100,200)—
Rectangle box2 = box1;still 1(0,0,100,200)the same object
box1.grow(50, 50)1(-50,-50,200,300)the same, changed

grow makes the rectangle bigger by 50 units in all directions: it decreases x and y by 50 and increases width and height by 100. If we print box2 we should not be surprised to see that it has changed too, because it refers to the same object.

47. Two names, one Rectangle

Picture it

This is the same picture as Lesson 7a's aliased array. The difference is what happens next.

Figure (svg): Two variables box1 and box2 each with an arrow to the same Rectangle object after grow has changed it

This scenario is called aliasing, because a single object has multiple names that refer to it. As you can tell from this simple example, code that involves aliasing can get confusing fast, and it can be difficult to debug.

48. Why immutability made this harmless before

Worked example

Two variables referring to one String was completely safe in Chapter 9. Two referring to one Rectangle is not. Compare them directly.

// Chapter 9: immutable
String s1 = "hello";
String s2 = s1;
s1 = s1.toUpperCase();       // s1 points somewhere NEW
System.out.println(s2);      // hello - unaffected

// Chapter 10: mutable
Rectangle b1 = new Rectangle(0, 0, 100, 200);
Rectangle b2 = b1;
b1.grow(50, 50);             // the OBJECT changes
System.out.println(b2);      // changed too
StringRectangle
can the object change?noyes
what does the method do?returns a new objectmodifies this one
what does the second variable see?the original, unchangedthe change
is aliasing a problem?noyes

Notice what changed in each case.

Why: For the String, the variable was reassigned. For the Rectangle, the object was modified.

Notice that both had two references.

Why: Aliasing is present in both examples. Only one of them causes trouble.

Conclude what actually causes the problem.

Why: Not aliasing on its own — aliasing plus mutability. Either one alone is harmless.

Note the design consequence.

Why: Immutable objects can be shared freely; mutable ones need you to know who else holds a reference.

Verify: Run both and confirm s2 is unchanged while b2 is not.

Why: That comparison is the whole answer to why did Chapter 9 spend so long on immutability? — it was building the contrast this section needs.

49. What does box2 show?

Prediction

Two variables, one object.

Rectangle box1 = new Rectangle(0, 0, 100, 200);
Rectangle box2 = box1;
box1.translate(10, 20);
System.out.println(box2.x);
stepobjectsbox2.x
Rectangle box2 = box1;10
box1.translate(10, 20)110 — the same object

Predict first

What is displayed?

  • 10
  • 0
  • 20
  • a compile error

Correct: 10

Why: The assignment copied the reference, so box1 and box2 are aliases for one Rectangle, and translating it through either name changes the single object both refer to. Counting objects rather than variables is what makes this predictable — there was only ever one.

50. What makes aliasing hard to debug

Concept

The difficulty is not that the behaviour is complicated. It is that the cause and the symptom appear in different places.

what you seewhere the cause is
box2 changed and I never touched box2a line that mentions only box1
the object changed inside a methoda call that looks like it only reads
a 'backup' changed with the originalthe assignment that made it, pages earlier
two parts of the program disagreeone of them modified shared state

In each row, searching for the variable that looks wrong finds nothing — the change was made through a different name. **The question to ask is not what changed this variable? but who else has a reference to this object?** That reframing is most of debugging with mutable objects.

51. Making a copy that is not a copy

Trap

The trap

The same mistake as Lesson 7a's array backup, now with an object.

Rectangle original = new Rectangle(0, 0, 100, 200);
Rectangle backup = original;      // NOT a copy

original.translate(50, 50);
System.out.println(backup);
// x=50, y=50 - the "backup" moved too
stepobjects in existencebackup shows
Rectangle backup = original;1(0, 0, ...)
original.translate(50, 50)1(50, 50, ...) — changed

There was never a backup. The assignment copied the reference, so both names point at the one Rectangle — and the original values are gone.

The fix

Create a second object.

Rectangle original = new Rectangle(0, 0, 100, 200);
Rectangle backup =
    new Rectangle(original.x, original.y,
                  original.width, original.height);

original.translate(50, 50);
System.out.println(backup);      // x=0, y=0 - unaffected
approachobjectsindependent?
backup = original1no — aliases
new Rectangle(original.x, ...)2yes

The test that distinguishes them is the same one from Lesson 7a: change one and look at the other. And the same question applies — count objects, not variables.

52. Aliasing: harmless or hazardous?

Definition probe

It depends entirely on whether the object can change.

Sort into buckets

Sort each situation.

harmless
two variables referring to one String; two variables referring to one Integer
a hazard
two variables referring to one Rectangle; two variables referring to one array
safe
The object is immutable, so no one can change it through any reference. Sharing costs nothing and risks nothing.
risk
The object is mutable, so a change made through one name is visible through every other — and the line that made the change may not mention the name you are looking at.

53. Watch one object and two names

Invariant

Step through the aliasing example, counting objects at each frame.

Step through it

At which frame does a second object come into existence?

  1. One object exists, and box1 refers to it.
  2. Still ONE object. box2 now refers to it as well — two arrows, one target.
  3. grow modifies that single object: x and y drop by 50, width and height rise by 100.
  4. Both print the same thing, because both names lead to the same changed object.

None of them. There is exactly one Rectangle throughout, and that is the whole explanation — every surprise in this example comes from expecting there to be two.

54. Would immutable Rectangles be better?

Counterexample

Chapter 9 argued hard for immutability. Argue the other way.

Discussion prompt

If Rectangle were immutable, translate would have to return a new Rectangle and aliasing would be harmless. Why do you think Java's designers made it mutable instead?

Hint: Think about a program moving a hundred rectangles sixty times a second.

Answer:

Efficiency and naturalness. Sometimes it is more efficient to modify an existing object than to create a new one, and some computations are expressed more naturally using mutation — move this rectangle really is an action performed on a thing.

A graphics program moving many shapes many times a second would allocate an enormous number of short-lived objects under an immutable design, all of which would then have to be collected.

Think Java's own conclusion is the right one: neither design is always better, which is why you will see both. What matters is knowing which kind of object you are holding — and Section 10.10 makes that explicit.

55. What changes, and what the caller sees

Comparison

Four operations on an object parameter. Fill the blanks.

Comparison matrix

inside a method taking Rectangle boxchanges the object?the caller sees it?
int w = box.width;nothere is nothing to see
box.width = 50;yesyes
box.translate(5, 5);yesyes
box = new Rectangle(...);no — a different objectno — only the method's own reference moved

The last row is the one people get wrong in both directions. Assigning to the parameter moves the arrow; assigning to an attribute changes what the arrow points at.

56. The pattern to carry away

Pattern

Every question about objects in this chapter is answered by drawing the diagram and counting the objects.

Rectangle a = new Rectangle(0, 0, 100, 200);   // 1 object
Rectangle b = a;                                // still 1
Rectangle c = new Rectangle(a.x, a.y,
                            a.width, a.height); // now 2

a.translate(10, 10);
// b moved.  c did not.
questionhow to answer it
will this change be visible elsewhere?count the references to the object
is this a copy or an alias?count the objects, not the variables
does this method modify its argument?look for an attribute on the left of an assignment
why did a variable I never touched change?ask who else has a reference

57. Check: reading attributes

Check

Work it out before you click.

Point p = new Point(6, 8);
System.out.println(p.x * p.y);
expressionvalue
p.x6
p.y8
p.x * p.y48

Check your understanding

What is displayed?

  • A. 48 (correct)
  • B. 68
  • C. 14
  • D. a compile error — x and y are private

Answer: A

Why: Both attributes are public ints, so p.x * p.y is ordinary multiplication of 6 and 8. Point's attributes are deliberately public, which is what lets you reach them with dot notation — Chapter 11 introduces private attributes and the reasons for preferring them.

Why B tempts people
This would be concatenation, which happens only when one operand is a String. Both operands here are ints.
Why C tempts people
This adds the attributes rather than multiplying them.
Why D tempts people
Point's x and y are public, which is precisely why this chapter can use them to introduce attributes.

58. Check: modifying through a parameter

Check

Work it out before you click.

public static void widen(Rectangle r) {
    r.width = r.width + 10;
}

Rectangle box = new Rectangle(0, 0, 100, 200);
widen(box);
System.out.println(box.width);
stepbox.width
new Rectangle(...)100
r refers to the same object100
r.width = r.width + 10110

Check your understanding

What is displayed?

  • A. 110 (correct)
  • B. 100
  • C. 10
  • D. a compile error

Answer: A

Why: The parameter receives a copy of the reference, so r and box refer to the same Rectangle, and assigning to r.width changes that one object. The method returns void and still has a visible effect, which is what makes mutable objects both convenient and hard to track.

Why B tempts people
This assumes the method received its own copy of the Rectangle, but only the reference was copied.
Why C tempts people
This is the amount added, not the resulting width.
Why D tempts people
Passing an object to a method and modifying its attributes is entirely legal.

59. Check: aliasing

Check

Work it out before you click.

Rectangle a = new Rectangle(0, 0, 10, 10);
Rectangle b = a;
Rectangle c = new Rectangle(0, 0, 10, 10);
a.translate(5, 5);
variablerefers tox after
athe first object5
bthe same first object5
ca second object0

Check your understanding

What are b.x and c.x?

  • A. b.x is 5 and c.x is 0 (correct)
  • B. both are 5
  • C. both are 0
  • D. b.x is 0 and c.x is 5

Answer: A

Why: b = a copies the reference, so b is an alias and sees the translation. c was created with its own new, so it is a separate object with the same initial values and is unaffected. Two objects exist, and only one of them moved.

Why B tempts people
This assumes c is also an alias, but new created a second, independent object.
Why C tempts people
This assumes b is a copy, but plain assignment between object variables never copies the object.
Why D tempts people
This reverses the two: it is b that aliases a, and c that is independent.

60. Why 'who else has a reference?' is the debugging question

Real world

Aliasing bugs share a signature: a value changes and the line that changed it never mentions the variable you are watching.

Discussion prompt

You are debugging a program where a Rectangle's width changes unexpectedly between two of your own lines. Searching for that variable's name finds nothing that assigns to it. What is your next move?

Hint: The variable did not change. Something else did.

Answer:

Find every other reference to that object. Where was it created, what was it assigned to, and what methods was it passed to? Any of those could have modified it through a different name.

That is why the memory diagram is the debugging tool here rather than the source code. The source shows names; the diagram shows objects, and the object is what changed.

The practical defence is to limit sharing. Prefer immutable objects where you can; where you cannot, copy an object before handing it to something you do not control, and be deliberate about which methods modify their arguments. Chapter 11 gives you the tool that makes this enforceable — private attributes.

61. How sure are you?

Commit first

Commit to an answer and to your confidence.

Predict first

A method takes a Rectangle and executes box = new Rectangle(0, 0, 1, 1);. What does the caller see?

  • Nothing — only the method's own parameter variable was reassigned
  • The caller's rectangle becomes 1 by 1
  • The caller's variable becomes null
  • A compile error

Correct: Nothing — only the method's own parameter variable was reassigned

Why: Parameter passing copies the reference, so the method has its own variable pointing at the caller's object. Assigning a new object to that variable moves the method's arrow and leaves the caller's variable pointing where it always did. Contrast this with box.x = 0;, which follows the arrow and changes the shared object — and the caller sees that immediately. This is exactly the boundary question from Lesson 7a: assigning to the parameter moves the arrow; assigning to an attribute changes what it points at.

62. Explain it to someone else

Explain it

Two minutes, drawing as you go.

Discussion prompt

A classmate wrote Rectangle backup = original; and cannot understand why moving the original also moved the backup. Draw the diagram and explain. Then explain why the same mistake with a String would have been harmless.

Hint: Count objects in both cases.

Answer:

Draw two variable boxes and one Rectangle. The assignment copied the arrow, not the object, so both names lead to the same thing — and translating it through either name changes the one object.

With a String the same assignment also gives two names for one object. But a String cannot be changed, so there is no way for one name to affect what the other sees. The only thing you can do is point one variable at a different string, which leaves the other alone.

The sentence worth landing on: aliasing is only a problem when the object is mutable. That is why Chapter 9 came first.

63. Exit ticket

Exit ticket

One question before you close the deck.

Predict first

Why is aliasing a hazard for Rectangles but not for Strings?

  • Because Rectangles are mutable, so a change through one reference is visible through every other
  • Because Strings cannot be assigned to more than one variable
  • Because Strings are primitives
  • Because Rectangle does not have an equals method

Correct: Because Rectangles are mutable, so a change through one reference is visible through every other

Why: Both Strings and Rectangles are objects, and both can have several variables referring to one of them — aliasing happens equally in each case. The difference is that a Rectangle's attributes can be modified, so a change made through one name appears through all of them, while a String cannot be changed at all. The hazard is aliasing plus mutability; either one on its own is harmless, which is exactly why immutable objects can be shared freely.

64. Draw the whole lesson

Connect it up

One page, from memory.

Draw it

Draw three diagrams. First: Rectangle a = new Rectangle(0, 0, 100, 200); — one variable, one object, four attributes labelled. Second: after Rectangle b = a; — mark clearly how many objects exist. Third: after a.translate(50, 50); — show what changed and note what b.x now reports. Underneath, write the two lines box = new Rectangle(...) and box.x = 0 as they would appear inside a method, and say for each whether the caller sees anything.

65. Recap

Recap

Five sections that introduce objects whose data you can reach and change — and the debugging difficulty that comes with it.

if you remember one thingit is this
about attributesthe dot reaches data as well as methods
about parametersthe reference is copied, not the object
about debuggingask who else has a reference

Sources

  1. Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 10 (Mutable Objects), Sections 10.1-10.5, pp. 167-173
  2. Java SE 21 API — java.awt.Point
  3. Think Java 2e — free online edition and source code

Want this taught 1-on-1? Alexander tutors Java — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108