Methods: How to Write Them
Why this matters
Imagine you're at a high-tech vending machine. You want a soda. You press the button for "Cola" (that's like calling a method). The machine whirs, and a can of soda drops into the slot. You received something back—a value! Now, imagine you press a button to "Add $1 to Credit." The screen updates, but nothing comes out of the machine. It just changed its internal state.
In Java, our objects are like that vending machine. We need to write the instructions—the methods—that tell them what to do when we "press their buttons." Some methods will give us a value back (like the soda), while others will just change the object's data (like adding credit).
In this lesson, we'll learn how to write both kinds of methods, giving our objects the behaviors they need to be useful.
Concept overview
flowchart TD
A[Start: Main Program] --> B{Call myObject.doTask(5)};
B --> C[Control transfers to doTask method];
C --> D[Parameter 'x' in doTask gets a copy of value 5];
D --> E[doTask method executes its logic];
E --> F{Is doTask a void method?};
F -- Yes --> G[Control returns to Main Program];
F -- No --> H[Calculate return value, e.g., 'result'];
H --> I[Return 'result' and control to Main Program];
G --> J[Main Program continues execution];
I --> J;
J --> K[End];
Core explanation
Alright, let's dive into writing the code that brings our objects to life. If instance variables are the nouns (the data an object has), methods are the verbs (the actions an object can do).
The Two Flavors of Methods: void and Non-void
Every method you write will fall into one of two categories, and the difference is simple: does it give you a value back when it's done?
1. void Methods: The Do-ers
A void method is like telling a friend, "Hey, can you please go turn on the light?" They perform an action, but they don't hand you anything back. The job is just... done.
In Java, the keyword void in a method's signature tells us that this method will not return any value.
public class LightSwitch {
private boolean isOn;
// A void method that performs an action
public void flipSwitch() {
isOn = !isOn; // Toggles the boolean value
System.out.println("Click!");
}
}
Notice the public void flipSwitch(). The void keyword is your signal. This method's only job is to change the isOn instance variable. When you call it, it does its work and that's it.
These are often mutator methods (or "setters"). A mutator's purpose is to mutate, or change, an object's state—specifically, the values of its instance variables.
2. Non-void Methods: The Givers
A non-void method is like asking a friend, "What's the temperature outside?" They check, and then they tell you a specific value, like "68 degrees." They are giving you information back.
Instead of void, the method signature will specify the type of data it promises to return. This could be int, double, boolean, String, or any other type.
public class Thermometer {
private double currentTempF;
// A non-void method that returns a value
public double getTemperature() {
return currentTempF;
}
}
Here, public double getTemperature() promises to return a double. And it delivers on that promise with the return statement.
These are often accessor methods (or "getters"). An accessor's job is to provide access to an object's state without changing it. It gives you a copy of the value of an instance variable.
The return Statement: Handing Back the Result
In every non-void method, you must have a return statement that provides a value matching the declared return type.
Think of return as the final act of a method. It does two things:
- It hands a value back to wherever the method was called.
- It immediately stops the method and returns control flow back to the caller.
public int addFive(int number) {
return number + 5;
// System.out.println("This will never print!"); // ERROR: unreachable code
}
Once that return happens, the addFive method is finished.
Using Parameters: Giving Methods What They Need
What if a method needs some information to do its job? When you order a pizza, you have to tell them what toppings you want. Those toppings are parameters.
Parameters are variables listed inside the method's parentheses. When you call the method, you provide values for them, which are called arguments.
public class BankAccount {
private double balance;
// A mutator method with a parameter
public void deposit(double amount) {
if (amount > 0) {
balance = balance + amount;
}
}
// A non-void method with a parameter
public double calculateFutureValue(int years) {
// A simple (and not very accurate) interest calculation
double futureValue = balance * (1 + 0.02 * years);
return futureValue;
}
}
When we call myAccount.deposit(100.50), the value 100.50 is the argument. Inside the deposit method, the parameter variable amount is initialized with the value 100.50.
The Golden Rule of Primitives: Pass-by-Copy
This next concept is critical for the AP exam, so let's be very clear.
When you pass a primitive type (int, double, boolean, char) as an argument to a method, Java makes a copy of the value.
The method receives the copy, not the original variable. It's like you wrote a number on a sticky note, and you hand a photocopy of that note to your method. The method can scribble all over the photocopy, but your original sticky note remains unchanged.
Let's see this in action.
public class Tester {
public void tryToChange(int x) {
x = x + 10; // We are changing the copy
System.out.println("Inside method, x is: " + x); // Prints 15
}
public static void main(String[] args) {
Tester myTester = new Tester();
int myNumber = 5;
System.out.println("Before method call, myNumber is: " + myNumber); // Prints 5
myTester.tryToChange(myNumber); // Pass the value of myNumber
System.out.println("After method call, myNumber is: " + myNumber); // Still prints 5!
}
}
See it in action
Worked examples
Let's build a class from scratch to see these concepts in action. We'll create a GamePlayer class for a simple arcade game.
Example 1: The GamePlayer Class
Problem: Create a GamePlayer class that tracks a player's username and score. It should have:
- A way to get the player's score.
- A way to increase the player's score by a specific amount.
- A way to reset the score back to zero.
Solution Walkthrough:
First, let's define the class and its instance variables.
public class GamePlayer {
private String username;
private int score;
// Constructor to initialize the player
public GamePlayer(String name) {
this.username = name;
this.score = 0; // Players always start with 0 points
}
Now, let's tackle the requirements one by one.
- 1Get the player's scoreThis sounds like we need to access data. This calls for an accessor method. It needs to give us a value back (the score), so it will be non-
void. The score is anint, so the method must return anint.// Accessor method ("getter") public int getScore() { return this.score; }Why? We use
return this.score;to send a copy of thescorevalue back to whoever called the method. It'spublicso code outside this class can use it. - 2Increase the scoreThis action changes the player's state. This is a job for a mutator method. It needs to know how much to increase the score by, so it will need a parameter. Since its job is just to change the
scoreand not to report anything back, it should bevoid.// Mutator method public void increaseScore(int points) { if (points > 0) { this.score = this.score + points; } }Why? We declare it
voidbecause it doesn't return a value. Theint pointsparameter receives the value passed in when the method is called. We updatethis.scoreusing that value. - 3Reset the scoreThis is another action that changes state, so it's another mutator. It doesn't need any input, it just sets the score to 0. It's a
voidmethod.// Mutator method public void resetScore() { this.score = 0; }Why? Simple and direct. It changes the instance variable
scoreto 0. No return value is needed.
Putting it all together in a main method:
public static void main(String[] args) {
GamePlayer player1 = new GamePlayer("Maya");
System.out.println("Initial score: " + player1.getScore()); // Prints 0
player1.increaseScore(50);
System.out.println("Score after first round: " + player1.getScore()); // Prints 50
player1.increaseScore(25);
System.out.println("Score after second round: " + player1.getScore()); // Prints 75
player1.resetScore();
System.out.println("Score after reset: " + player1.getScore()); // Prints 0
}
Try it yourself
Ready to write your own? Let's model a CoffeeMug.
Your Task:
Create a CoffeeMug class with a private double ounces; instance variable.
Write the following methods:
- A constructor
public CoffeeMug(double startOunces)that initializes the mug. - An accessor method
public double getOunces()that returns the current amount of coffee. - A
voidmutator methodpublic void takeSip(double sipSize)that decreases the ounces in the mug. Make sure the ounces can't go below zero!
Practice — 8 questions
In simple terms, methods are a class's action verbs, letting objects perform tasks like calculating a value or changing their internal data.
public class LightSwitch {
private boolean isOn;
// A void method that performs an action
public void flipSwitch() {
isOn = !isOn; // Toggles the boolean value
System.out.println("Click!");
}
}
- 3.5.A: Develop code to define behaviors of an object through methods written in a class using primitive values and determine the result of calling these methods.
- 3.5.A.1
- A void method does not return a value. Its header contains the keyword void before the method name.
- 3.5.A.2
- A non-void method returns a single value. Its header includes the return type in place of the keyword void.
- 3.5.A.3
- In non-void methods, a return expression compatible with the return type is evaluated, and the value is returned. This is referred to as return by value.
- 3.5.A.4
- The return keyword is used to return the flow of control to the point where the method or constructor was called. Any code that is sequentially after a return statement will never be executed. Executing a return statement inside a selection or iteration statement will halt the statement and exit the method or constructor.
- 3.5.A.5
- An accessor method allows objects of other classes to obtain a copy of the value of instance variables or class variables. An accessor method is a non-void method.
- 3.5.A.6
- A mutator (modifier) method is a method that changes the values of the instance variables or class variables. A mutator method is often a void method.
- 3.5.A.7
- Methods with parameters receive values through those parameters and use those values in accomplishing the method's task.
- 3.5.A.8
- When an argument is a primitive value, the parameter is initialized with a copy of that value. Changes to the parameter have no effect on the corresponding argument.
flowchart TD
A[Start: Main Program] --> B{Call myObject.doTask(5)};
B --> C[Control transfers to doTask method];
C --> D[Parameter 'x' in doTask gets a copy of value 5];
D --> E[doTask method executes its logic];
E --> F{Is doTask a void method?};
F -- Yes --> G[Control returns to Main Program];
F -- No --> H[Calculate return value, e.g., 'result'];
H --> I[Return 'result' and control to Main Program];
G --> J[Main Program continues execution];
I --> J;
J --> K[End];
Read what Saavi narrates
Hey everyone, it’s Saavi. Let’s talk about methods.
Imagine you're at a high-tech vending machine. You press the button for "Cola"... that's like calling a method. The machine whirs, and a can of soda drops into the slot. You got something back—a value! Now, what if you press a button to "Add one dollar to Credit"? The screen updates, but nothing comes out. The machine just changed its internal state.
In Java, our objects are like that vending machine. We need to write the instructions, the methods, that tell them what to do. Some methods give us a value back, like the soda. Others just change the object's data, like adding credit.
Let's look at a quick example. Imagine a GamePlayer class that tracks a player's score.
We'd want a method to increase the score. We could call it increaseScore, and it would need to know by how many points. So we'd give it a number. This method's job is just to change the score, not to give anything back. We call this a void method.
But what if we want to display the score on the screen? We need a method that gives us the score. We could call it getScore. This method's job is to hand back a value... the integer representing the current score. This is a non-void method.
So in our main program, we could create a player named Maya. We'd call player one dot increaseScore and pass in 50. Her score is now 50. Then we could call it again, passing in 25. Her score is now 75. To see it, we'd print out the result of calling player one dot getScore.
Here's a really common mistake students make. They try to write something like... "int newScore equals player one dot increaseScore fifty". But that won't work! The increaseScore method is void, it doesn't give a value back, so you can't assign it to a variable. You just call it on its own line to make it do its job.
Methods are the heart of what makes our objects useful. Once you get the hang of writing them, you're well on your way to building powerful programs. You've got this!
If you promise to return a value (e.g., `public int getScore()`), you *must* have a `return` statement in every possible path through the method. The compiler will give you a "missing return statement" error.
Ensure every non-`void` method ends with `return someValue;` where `someValue` matches the declared return type.
`void` means "returns nothing." Trying to store "nothing" in a variable (e.g., `int myVal = myObject.doSomething();` where `doSomething` is `void`) is a type mismatch and a logical error.
Call `void` methods on their own line: `myObject.doSomething();`. Don't try to assign the result.
The `return` keyword immediately exits the method. Any code placed after it in the same block is unreachable and will be flagged as an error by the compiler.
Place the `return` statement as the last action in a logical path. Any cleanup or logging must happen *before* the `return`.
`public int getPrice() { return 9.99; }` is an error. You promised an `int` but tried to return a `double`. Java is strictly typed and requires an exact match (or a value that can be implicitly converted without losing information, which isn't the case here).
Make sure the value you return is the same type as the one in the method signature. Or, change the method signature to match the value you need to return.
Java uses pass-by-value for primitives. The method gets a *copy* of the value, not a reference to the original variable.
If you need a method to compute a new value based on an input, have it `return` the new value. Then, the calling code can decide whether to update the original variable with that returned value: `myNumber = myObject.calculateNewValue(myNumber);`.