Fiveable

๐Ÿ’ปAP Computer Science A Unit 3 Review

QR code for AP Computer Science A practice questions

3.6 Accessor Methods

๐Ÿ’ปAP Computer Science A
Unit 3 Review

3.6 Accessor Methods

Written by the Fiveable Content Team โ€ข Last updated September 2025
Verified for the 2026 exam
Verified for the 2026 examโ€ขWritten by the Fiveable Content Team โ€ข Last updated September 2025
๐Ÿ’ปAP Computer Science A
Unit & Topic Study Guides
Pep mascot

Understanding how Java handles object references in methods is like understanding the difference between giving someone your house key versus giving them your entire house. When you pass an object to a method, you're passing a copy of the reference (the key), not a copy of the object itself (the house). This means the method can use that key to modify your actual object, which leads to some surprising behavior if you're not careful.

This concept trips up so many students because it's different from how primitives work. When you pass an int to a method, changes inside the method don't affect the original. But when you pass an ArrayList, the method can add, remove, or modify elements, and those changes persist after the method ends. Once you truly grasp this difference, a lot of Java's behavior suddenly makes sense.

The same principle applies to return values. When a method returns an object, it's returning the reference, not creating a new copy. This is efficient but means multiple parts of your program might be sharing the same object. Understanding these reference semantics is crucial for writing correct code and debugging mysterious behavior.

  • Major concepts: Reference parameters vs primitive parameters, mutable object modification, returning references, access restrictions with private data
  • Why this matters for AP: Critical for FRQ2 and FRQ3, common source of MCQ tricks, essential for understanding object behavior
  • Common pitfalls: Thinking objects are copied when passed, accidentally modifying parameters, privacy leaks through references
  • Key vocabulary: Object reference, parameter passing, mutable object, reference semantics, aliasing
  • Prereqs: Understanding objects vs primitives, basic method concepts, difference between reference and object

Key Concepts

Pep mascot
more resources to help you study

Reference Parameters - Sharing the Key, Not the House

When you pass an object as a parameter, Java copies the reference, not the object. Think of it like making a copy of your house key - both keys open the same door to the same house. The parameter and the argument point to the same object in memory, so changes through either reference affect the shared object.

This is fundamentally different from primitives. When you pass an int, Java copies the value. The parameter and argument are completely independent. But with objects, they're connected through the shared reference. This connection enables powerful patterns but also creates potential for bugs.

The key insight is that "pass by value" in Java always means copying what's in the variable. For primitives, that's the actual value. For objects, that's the reference value (the address). Java never passes the object itself - it always passes a copy of the way to find the object.

Modifying Mutable Objects Through Parameters

A mutable object is one whose state can be changed after creation. Arrays, ArrayLists, and most custom objects are mutable. When a method receives a reference to a mutable object, it can modify that object's state, and those changes persist after the method returns.

This power comes with responsibility. Good programming practice says don't modify parameter objects unless that's the method's explicit purpose. If your method is named calculateAverage, it shouldn't also sort the array. This unexpected side effect makes code hard to understand and debug.

Sometimes modifying the parameter is the whole point. A method like sortArray or addStudent is expected to modify its parameter. The method name and documentation should make this clear. When in doubt, leave parameters unchanged or work with a copy.

Returning References - Sharing Access to Internal State

When a method returns an object, it returns the reference, not a new copy. This is efficient - no need to duplicate potentially large objects. But it also means the caller now has direct access to what might be internal state of your class, which can break encapsulation.

Consider a getter method that returns an ArrayList. If you directly return your private ArrayList, the caller can modify it, potentially breaking your class's invariants. This is called a "privacy leak" - you've accidentally exposed internal state that should be protected.

The solution depends on your needs. Sometimes sharing is intentional - maybe you want changes to be reflected. Other times you should return a copy or an immutable view. The key is being intentional about whether you're sharing access or providing independent data.

Access Restrictions with Different Types

A subtle but important rule: methods can only access private members of parameters if the parameter is the same type as the enclosing class. A Student method can access private fields of other Student objects passed as parameters, but not private fields of Teacher objects.

This makes sense when you think about it. The Student class knows its own implementation details, so it can safely work with other Student instances' internals. But it doesn't know how Teacher is implemented internally, so those details stay hidden.

This rule enables certain patterns like copy constructors and comparison methods while maintaining encapsulation between different classes. It's a balance between flexibility for same-type operations and protection across type boundaries.

Code Examples

Let's see reference passing in action:

// Example: Understanding reference parameters
public class ReferenceDemo {
    public static void main(String[] args) {
        // With primitives - no connection after passing
        int x = 10;
        changeIntValue(x);
        System.out.println("x after method: " + x);  // Still 10

        // With arrays - reference creates connection
        int[] numbers = {1, 2, 3, 4, 5};
        changeArrayValue(numbers);
        System.out.println("numbers[0] after method: " + numbers[0]);  // Now 99!

        // With objects - same reference behavior
        Student student = new Student("Alice", 85);
        changeStudentGrade(student);
        System.out.println("Grade after method: " + student.getGrade());  // Now 90!
    }

    // Primitive parameter - receives copy of value
    public static void changeIntValue(int num) {
        num = 99;  // Only changes local copy
    }

    // Array parameter - receives copy of reference
    public static void changeArrayValue(int[] arr) {
        arr[0] = 99;  // Changes the actual array
    }

    // Object parameter - receives copy of reference
    public static void changeStudentGrade(Student s) {
        s.setGrade(90);  // Changes the actual student object
    }
}

class Student {
    private String name;
    private int grade;

    public Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    public int getGrade() { return grade; }
    public void setGrade(int grade) { this.grade = grade; }
}

Privacy leaks and proper encapsulation:

// Example: Avoiding privacy leaks when returning references
public class GradeBook {
    private ArrayList<Integer> grades;

    public GradeBook() {
        grades = new ArrayList<Integer>();
    }

    // BAD: Returns reference to internal list
    public ArrayList<Integer> getGradesBad() {
        return grades;  // Caller can modify our private list!
    }

    // GOOD: Returns copy to protect internal state
    public ArrayList<Integer> getGradesGood() {
        return new ArrayList<Integer>(grades);  // Safe copy
    }

    // ALSO GOOD: Return immutable view
    public List<Integer> getGradesReadOnly() {
        return Collections.unmodifiableList(grades);
    }

    // Method that intentionally modifies parameter
    public void addGrades(ArrayList<Integer> newGrades) {
        // Clear documentation that parameter will be modified
        for (Integer grade : newGrades) {
            if (grade >= 0 && grade <= 100) {
                grades.add(grade);
                newGrades.remove(grade);  // Modifying parameter!
            }
        }
    }

    // Better approach - don't modify parameter
    public void addGradesBetter(ArrayList<Integer> newGrades) {
        for (Integer grade : newGrades) {
            if (grade >= 0 && grade <= 100) {
                grades.add(grade);  // Only modify our own state
            }
        }
    }
}

Access to private members of same type:

// Example: Accessing private members of same class type
public class BankAccount {
    private double balance;
    private String accountNumber;

    public BankAccount(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    // Can access private members of OTHER BankAccount objects
    public void transfer(double amount, BankAccount other) {
        if (amount > 0 && amount <= this.balance) {
            this.balance -= amount;
            other.balance += amount;  // Direct access to private field!
        }
    }

    // Compare with another account
    public boolean hasMoreMoney(BankAccount other) {
        return this.balance > other.balance;  // Can read private field
    }

    // Copy constructor - common pattern using same-type access
    public BankAccount(BankAccount other) {
        this.accountNumber = other.accountNumber + "-COPY";
        this.balance = other.balance;  // Direct access to private fields
    }

    // But can't access private members of different types
    public void processLoan(LoanAccount loan) {
        // double loanBalance = loan.balance;  // ERROR: Can't access
        double loanBalance = loan.getBalance();  // Must use public method
    }
}

class LoanAccount {
    private double balance;  // Private to LoanAccount

    public double getBalance() {
        return balance;
    }
}

Common Errors and Debugging

Unintended Modification of Parameters

Accidentally modifying objects passed as parameters:

// BAD: Method has surprising side effect
public static double calculateAverage(ArrayList<Integer> scores) {
    Collections.sort(scores);  // OOPS! Modified the parameter

    double sum = 0;
    for (int score : scores) {
        sum += score;
    }
    return sum / scores.size();
}

// GOOD: Work with a copy if you need to modify
public static double calculateMedian(ArrayList<Integer> scores) {
    ArrayList<Integer> sorted = new ArrayList<>(scores);  // Copy first
    Collections.sort(sorted);  // Sort the copy, not original

    int middle = sorted.size() / 2;
    return sorted.get(middle);
}

Aliasing Issues

Multiple references to the same object causing confusion:

// PROBLEM: Two variables reference same list
ArrayList<String> list1 = new ArrayList<>();
list1.add("Hello");

ArrayList<String> list2 = list1;  // Not a copy! Same list
list2.add("World");

System.out.println(list1.size());  // Prints 2 - surprise!

// SOLUTION: Make explicit copy when needed
ArrayList<String> list3 = new ArrayList<>(list1);  // True copy
list3.add("!");
System.out.println(list1.size());  // Still 2
System.out.println(list3.size());  // 3

Privacy Leaks Through Getters

Accidentally exposing mutable internal state:

public class Team {
    private ArrayList<Player> players = new ArrayList<>();

    // BAD: Exposes internal list
    public ArrayList<Player> getPlayers() {
        return players;  // Caller can add/remove players!
    }
}

// External code can break invariants:
Team team = new Team();
team.getPlayers().clear();  // Removed all players!

// GOOD: Return defensive copy
public ArrayList<Player> getPlayers() {
    return new ArrayList<>(players);
}

Practice Problems

Problem 1: What does this code print?

public static void main(String[] args) {
    int[] arr = {1, 2, 3};
    mystery(arr);
    System.out.println(Arrays.toString(arr));
}

public static void mystery(int[] a) {
    a[0] = 99;
    a = new int[]{7, 8, 9};
    a[0] = 77;
}

Solution: Prints [99, 2, 3]

  • a[0] = 99 modifies the original array through the reference
  • a = new int[]{7, 8, 9} creates new array and changes local reference only
  • a[0] = 77 modifies the new array, not the original

Problem 2: Fix this class to prevent external modification:

public class StudentRecord {
    private ArrayList<Integer> testScores;

    public ArrayList<Integer> getTestScores() {
        return testScores;
    }
}

Solution:

public class StudentRecord {
    private ArrayList<Integer> testScores;

    // Option 1: Return copy
    public ArrayList<Integer> getTestScores() {
        return new ArrayList<>(testScores);
    }

    // Option 2: Return unmodifiable view
    public List<Integer> getTestScores() {
        return Collections.unmodifiableList(testScores);
    }

    // Option 3: Provide specific access methods
    public int getTestScore(int index) {
        return testScores.get(index);
    }

    public int getTestCount() {
        return testScores.size();
    }
}

Problem 3: Write a method that swaps the names of two Student objects. Is this possible?

Solution: No, you cannot swap the String names because:

  1. Strings are immutable - can't change the String itself

  2. You can't change which String object the name variable refers to from outside the class

  3. You could swap names if Student has a setName method:

public static void swapNames(Student s1, Student s2) {
    String temp = s1.getName();
    s1.setName(s2.getName());
    s2.setName(temp);
}

AP Exam Connections

Understanding reference semantics is crucial for the AP exam. Multiple choice questions love to test whether you understand what happens when objects are passed to methods or returned from methods. Always trace carefully through reference assignments and method calls.

For FRQs:

  • FRQ 2 (Class Design): Must properly handle object parameters and returns
  • FRQ 3 (Array/ArrayList): Often involves methods that modify or return lists
  • FRQ 4 (2D Array): Same concepts apply to 2D array references

Common exam patterns:

  • Tracing code that passes arrays/ArrayLists to methods
  • Identifying whether changes inside a method affect the original
  • Questions about privacy leaks through returned references
  • Understanding when references are shared vs independent

Key tip: Draw memory diagrams for tricky reference questions. Show which variables point to which objects. When a method is called, draw the parameter pointing to the same object as the argument. This visual approach helps avoid confusion about what modifications affect which objects.