Verified for the 2025 AP Computer Science A exam•Citation:
Methods with parameters are powerful tools that allow objects to receive information from the outside world and use it to perform actions or calculations. In a public class, these methods enable objects to interact with other parts of your program. Understanding how parameters work is essential for writing flexible, reusable code. This section explores how methods handle parameters, the distinction between formal and actual parameters, and the important concept of aliasing when working with object references. By mastering these concepts, you'll be able to design methods that effectively communicate with other parts of your program, whether you're working with primitive types like int and boolean or reference types like String and Student objects.
A method parameter is a variable that:
Java methods can accept parameters of any type—primitive types like int and boolean, or reference types like String and Student. This flexibility allows methods to work with a wide variety of data.
There are two important parameter-related terms to understand:
public class Example { // The parameter 'message' is a formal parameter public void displayMessage(String message) { System.out.println(message); } public void testMethod() { // "Hello, world!" is the actual parameter displayMessage("Hello, world!"); // A variable can also be an actual parameter String greeting = "Welcome!"; displayMessage(greeting); } }
Methods can only access the private data and methods of a parameter that is a reference to an object when:
public class Student { private String name; private int id; // This method can access privateField because otherStudent // is the same type as the enclosing class public boolean hasSameId(Student otherStudent) { // Can access otherStudent's private id field return this.id == otherStudent.id; } // This method cannot access Car's private fields public void drive(Car car) { // WRONG: Cannot access car's private fields // car.fuelLevel = 100; // CORRECT: Must use public methods to interact car.refuel(20); } }
Non-void methods with parameters:
public class Calculator { public int add(int a, int b) { return a + b; } public double calculateArea(double radius) { return Math.PI * radius * radius; } public String repeat(String text, int times) { String result = ""; for (int i = 0; i < times; i++) { result += text; } return result; } }
Java handles primitive and reference types differently when passing them to methods:
When an actual parameter is a primitive value (like int
, double
, or boolean
):
public class PrimitiveExample { public void modifyValue(int value) { value = value * 2; // Modifies the local copy only System.out.println("Inside method: " + value); } public void test() { int x = 10; System.out.println("Before method call: " + x); modifyValue(x); System.out.println("After method call: " + x); // Still 10, unchanged } }
When an actual parameter is a reference to an object:
public class ReferenceExample { public void modifyList(ArrayList<String> list) { list.add("New Item"); // Modifies the original list } public void test() { ArrayList<String> myList = new ArrayList<>(); myList.add("Original Item"); System.out.println("Before method call: " + myList); modifyList(myList); System.out.println("After method call: " + myList); // Shows the modified list } }
When passing a reference parameter, the formal parameter and the actual parameter become aliases—they both refer to the same object. This has important implications:
public class AliasingExample { public void modifyStudent(Student student) { // Both student and the original reference // point to the same Student object student.setName("Modified Name"); } public void test() { Student s1 = new Student("Original Name", 12345); System.out.println("Before: " + s1.getName()); modifyStudent(s1); System.out.println("After: " + s1.getName()); // Shows "Modified Name" } }
Here's a visual representation of what happens with reference parameters:
Before method call: +----------------+ s1 --------------> | Student object | | name: "Original Name" | +----------------+ During method call: +----------------+ s1 --------------> | Student object | | name: "Original Name" | student ----------> +----------------+ After modification: +----------------+ s1 --------------> | Student object | | name: "Modified Name" | student ----------> +----------------+
It is generally good programming practice:
// Approach 1: Modifying the parameter (use with caution) public void addItem(ArrayList<String> list, String item) { list.add(item); } // Approach 2: Creating and returning a new object (often better) public ArrayList<String> addItemToNewList(ArrayList<String> list, String item) { ArrayList<String> newList = new ArrayList<>(list); // Make a copy newList.add(item); return newList; }
Here are some common pitfalls to avoid:
Assuming primitive parameters can be modified:
// This will NOT work public void increment(int x) { x++; // Only modifies the local copy }
Reassigning reference parameters:
// This will NOT modify the original reference public void resetList(ArrayList<String> list) { list = new ArrayList<>(); // Creates a new list, original reference unchanged }
Forgetting that arrays are reference types:
// This WILL modify the original array public void fillWithZeros(int[] arr) { for (int i = 0; i < arr.length; i++) { arr[i] = 0; } }
Here's a complete example demonstrating various parameter concepts:
public class ParameterExample { // Method with primitive parameters public double calculateAverage(int a, int b, int c) { return (a + b + c) / 3.0; } // Method with array parameter (reference type) public double calculateArrayAverage(int[] numbers) { int sum = 0; for (int num : numbers) { sum += num; } return numbers.length > 0 ? (double) sum / numbers.length : 0; } // Method with object parameter (reference type) public void updateStudent(Student student, int newGrade) { if (student != null && newGrade >= 0 && newGrade <= 100) { student.addGrade(newGrade); } } // Method that creates a new object instead of modifying the parameter public Student createUpdatedStudent(Student original, String newName) { if (original == null) { return null; } // Create a new student with the same ID but different name return new Student(newName, original.getId()); } // Method demonstrating safe handling of mutable objects public ArrayList<Integer> getTopScores(ArrayList<Integer> scores, int topCount) { // Create a copy to avoid modifying the original ArrayList<Integer> scoresCopy = new ArrayList<>(scores); ArrayList<Integer> result = new ArrayList<>(); // Sort the copy (not the original) Collections.sort(scoresCopy, Collections.reverseOrder()); // Get the top scores int count = Math.min(topCount, scoresCopy.size()); for (int i = 0; i < count; i++) { result.add(scoresCopy.get(i)); } return result; } }