Free for students · Ad-free · WCAG 2.1 AA Compliant · Accessibility

Methods: Passing and Returning References of an Object

Lesson ~10 min read 8 MCQs

In simple terms: In simple terms, this topic is about how methods share or return access to existing objects, not brand new copies, like sharing a link to a Google Doc.

Why this matters

Have you ever worked on a group project using Google Docs? Imagine your friend, Priya, shares the link to your history presentation. You open the link and add a slide about the Boston Tea Party. When Priya opens the link again, she sees your new slide immediately. You both are looking at the exact same presentation.

Now, what if instead, Priya had emailed you a PowerPoint file? You download it, add your slide, and save it. Does Priya's original file have your new slide? Nope. You'd have to email your version back.

In Java, passing objects to methods is like sharing the Google Doc link. You're not sending a copy; you're sending a reference—a way to get to the one, original object. Understanding this is key to building programs that work together correctly. We'll explore how to pass these "links," how to return them, and a few important rules to avoid messing up the original "document."

Concept overview

flowchart TD
    subgraph main method
        A[Dog myDog = new Dog("Fido")];
    end

    subgraph The Heap (Memory)
        O1[Dog Object <br> name: "Fido"];
    end

    subgraph assignKennelName(Dog doggy)
        B[Parameter 'doggy' gets copy of reference];
        C[doggy.setName("Buddy")];
    end

    subgraph The Heap (Memory)
        O2[Dog Object <br> name: "Buddy"];
    end

    A -->|references| O1;
    A -- passes reference to --> B;
    B -->|also references| O1;
    C -- follows reference and modifies object --> O2;
    O1 -- state changes to --> O2;
This diagram shows the process of passing an object reference to a method. A "main method" box contains a variable `myDog` which points to a "Dog Object" in memory with the name "Fido". An arrow shows this reference being passed to a method, where the parameter `doggy` now also points to the same "Dog Object". The method then modifies the object, changing its name to "Buddy".

Core explanation

Hello everyone! I'm Saavi, and today we're going to tackle one of the most fundamental ideas in object-oriented programming: how objects travel between different parts of your code. Getting this right is the difference between a program that works perfectly and one with mysterious, frustrating bugs.

The Google Doc Analogy: References vs. Copies

Let's stick with our Google Doc analogy. In Java, primitive types (int, double, boolean) are like emailing a file. When you pass an int to a method, the method gets a completely separate copy of its value.

void changeValue(int x) {
  x = 100; // This only changes the method's local copy.
}

// In main method...
int myNumber = 10;
changeValue(myNumber);
System.out.println(myNumber); // This will still print 10!

Objects, however, are like sharing a Google Doc link. The variable you use to hold an object doesn't contain the object itself; it contains a reference—the address where the object lives in memory.

When you pass an object to a method, you're giving the method a copy of the reference. Both the original variable and the method's parameter now point to the exact same object in memory.

Primitive types pass values (copies), while objects pass references (links to the original).

Passing References to Methods

Let's see this with a Dog class.

The `Dog` class structure with its `name` field and methods.
public class Dog {
    private String name;

    public Dog(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

Now, let's write a kennel method that tries to rename a Dog.

public class Kennel {
    public void assignKennelName(Dog doggy) {
        // We are using the reference 'doggy' to call a method
        // on the original Dog object.
        doggy.setName("Buddy");
    }
}

// In your main method...
Dog myDog = new Dog("Fido");
System.out.println("My dog's name is: " + myDog.getName()); // Prints "Fido"

Kennel myKennel = new Kennel();
myKennel.assignKennelName(myDog); // We pass the reference to myDog

System.out.println("My dog's new name is: " + myDog.getName()); // Prints "Buddy"

Look at that! The assignKennelName method successfully changed the name of our original Dog object. This is because the doggy parameter inside the method and the myDog variable in main were both pointing to the same Dog object. When we called doggy.setName("Buddy"), we were following the reference to the original object and changing its state. This is what the College Board means when it says a method can "alter the state of the object."

public void tryToReplaceDog(Dog doggy) {
    // This creates a brand new Dog object.
    Dog newDog = new Dog("Max");
    // This makes the 'doggy' parameter point to the NEW dog.
    // It does NOT change what 'myDog' in main points to.
    doggy = newDog;
}

// In your main method...
Dog myDog = new Dog("Fido");
System.out.println("My dog's name is: " + myDog.getName()); // Prints "Fido"

Kennel myKennel = new Kennel();
myKennel.tryToReplaceDog(myDog);

System.out.println("Did my dog's name change? " + myDog.getName()); // Still prints "Fido"!

Why didn't it change? Because when you do doggy = newDog, you're only changing the local parameter doggy. You made it hold the address of a new Dog object named "Max." You did not change the original myDog variable back in main. It still holds the address of the "Fido" object. You just broke the link.

Returning References from Methods

This works the other way, too. When a method returns an object, it's returning a reference.

Imagine a method that finds the student with the highest GPA from a list.

// In a School class
public Student findValedictorian(Student[] students) {
    Student topStudent = students[0];
    for (int i = 1; i < students.length; i++) {
        if (students[i].getGpa() > topStudent.getGpa()) {
            topStudent = students[i]; // 'topStudent' now holds the reference to a different student
        }
    }
    // We are returning a REFERENCE to the student who was already in the array.
    // We are NOT creating a new copy of the valedictorian.
    return topStudent;
}

When you call this method, the variable you assign the result to gets a reference to the original Student object that exists in the students array.

Student valedictorian = mySchool.findValedictorian(allStudents);
// 'valedictorian' and the student in the 'allStudents' array are one and the same.

A Special Rule: Accessing Private Data

Normally, you can't access private instance variables from outside a class. But there's a special exception. A method can access the private data of a parameter if the parameter is the same class type as the method itself.

This sounds confusing, but it's essential for methods like equals().

Let's add an equals method to our Dog class to see if two Dog objects have the same name.

public class Dog {
    private String name;
    // ... constructors and other methods ...

    // This method compares the current Dog object ('this')
    // to another Dog object ('other').
    public boolean equals(Dog other) {
        // Because 'other' is also a Dog, we are allowed
        // to access its private 'name' variable directly!
        return this.name.equals(other.name);
    }
}
Tracing how `assignKennelName` modifies the `Dog` object through its reference.

See it in action

Java Code

      
      
Call Stack
Stack is empty — waiting for first call.
Step 0 / 0

Worked examples

Let's walk through a couple of examples to make sure this is crystal clear.

Example 1

Updating an Order in a System

Problem: You're working on a system for a coffee shop in Seattle. You have an Order class. You need to write a method in your Register class that marks an order as "complete."

// The Order class
public class Order {
    private int orderId;
    private boolean isComplete;

    public Order(int orderId) {
        this.orderId = orderId;
        this.isComplete = false; // All new orders start as not complete
    }

    public void setComplete(boolean status) {
        this.isComplete = status;
    }

    public boolean isComplete() {
        return this.isComplete;
    }
}

// The Register class
public class Register {
    // This is the method we need to understand
    public void fulfillOrder(Order currentOrder) {
        currentOrder.setComplete(true);
    }
}

Walkthrough:

  1. 1
    Setup
    Let's say our main method creates an Order object.
    Order order12 = new Order(12);
    System.out.println("Order 12 complete? " + order12.isComplete()); // Prints "false"

    At this point, order12 is a variable holding a reference (an address) to an Order object in memory. That object's isComplete field is false.

  2. 2
    Calling the Method
    Now, we create a Register and call our method.
    Register frontRegister = new Register();
    frontRegister.fulfillOrder(order12);

    Why this step matters: When fulfillOrder is called, Java takes the reference stored in order12 and copies it into the method's parameter, currentOrder. Now, both order12 (in main) and currentOrder (in fulfillOrder) point to the exact same Order object.

  3. 3
    Inside the Method
    The line currentOrder.setComplete(true); executes. Java follows the reference in currentOrder to the Order object and calls its setComplete method. This changes the isComplete instance variable inside that single object from false to true.
  4. 4
    The Result
    After the method finishes, we check the status again in main.
    System.out.println("Order 12 complete? " + order12.isComplete()); // Prints "true"

    The change is visible! We successfully modified the original object's state because we were working with a reference to it.

No need to return an object if you're modifying it via a passed reference.
Example 2

Comparing Teammates

Problem: You have a Player class for a basketball team. Write a method isSamePosition that checks if another player plays the same position.

public class Player {
    private String name;
    private String position; // e.g., "Guard", "Forward"

    public Player(String name, String position) {
        this.name = name;
        this.position = position;
    }

    // The method we're focusing on
    public boolean isSamePosition(Player otherPlayer) {
        // We can access otherPlayer.position directly!
        return this.position.equals(otherPlayer.position);
    }
}

Walkthrough:

  1. 1
    Setup
    We create two Player objects.
    Player maya = new Player("Maya", "Guard");
    Player jordan = new Player("Jordan", "Guard");
    Player carlos = new Player("Carlos", "Forward");
  2. 2
    Calling the Method
    We call the method on one object, passing another as the argument.
    boolean mayaAndJordan = maya.isSamePosition(jordan); // should be true
    boolean mayaAndCarlos = maya.isSamePosition(carlos); // should be false
  3. 3
    Inside the Method (First Call)
    When maya.isSamePosition(jordan) is called:
    • this refers to the maya object.
    • otherPlayer is a reference to the jordan object.
    • The code evaluates this.position.equals(otherPlayer.position).
    • Why this works: Even though position is private, the isSamePosition method is part of the Player class. The parameter otherPlayer is also a Player. Therefore, the rule allows us to access otherPlayer.position.
    • It compares "Guard".equals("Guard"), which is true. The method returns true.
  4. 4
    Inside the Method (Second Call)
    When maya.isSamePosition(carlos) is called, it compares "Guard".equals("Forward"), which is false. The method returns false.
The `Order` and `Register` classes, showing how `Register` interacts with `Order` objects.
Tracing the `fulfillOrder` method's effect on an `Order` object.

Try it yourself

Let's put your knowledge to the test.

Problem 1: You have a Song class and a Playlist class. The Playlist has an instance variable for the currentlyPlaying song.

public class Playlist {
    private Song currentlyPlaying;
    // ... constructor ...

    public void setCurrentlyPlaying(Song newSong) {
        // Your code here
    }

    public Song getCurrentlyPlaying() {
        return this.currentlyPlaying;
    }
}

In a main method, create a Playlist and two Song objects, songA and songB. The playlist starts with songA playing. Call a method skipSong(Playlist p, Song next) that should change the playlist's currently playing song to songB.

Problem 2: Add a method to your Song class called hasSameArtist(Song other). It should return true if the other song has the same artist. Assume Song has a private String artist instance variable. Can you write the method body by accessing other.artist directly?

The `Song` and `Playlist` classes for the 'Try It' problems.