Learn Java

Lesson 12Phase 2: Objects

Methods: parameters and return values

Giving objects behavior, passing values in, getting values back, and how arguments are copied.

By Updated 60 min

  • instance methods
  • parameters
  • return
  • void
  • arguments
  • pass by value

You can now give an object data: a student with a name and an age, a customer with a balance. But an object that only holds data is half an object. The other half is what it can do. This lesson is about giving objects behavior, the actions that live right next to the data they work on.

By the end of this lesson
  • Write a method inside a class: a return type, a name, parameters, and a body.
  • Use parameters to pass values into a method, and return to pass one back out.
  • Explain what void means and when a method should have it.
  • Predict what happens to a caller's variable when it is passed to a method (pass by value).
  • Define two methods with the same name but different parameters: overloading.
From before
You already know how to write a class with fields and make objects from it with new, and to read and set those fields with the dot. You also know variables and types. That is everything you need. We will not touch constructors or inheritance yet; this lesson is only about methods.

Behavior belongs with the data#

Here is a Rectangle class with two fields. Suppose we want its area. Width times height. We could compute that out in main, but the area of a rectangle is so obviously a thing a rectangle does that it belongs inside the class, sitting right next to the data it needs.

An action that lives inside a class is called a method. You have already met two: main is a method, and so is the deposit you saw in why objects. Now we write our own. Read this, then press Run.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 Rectangle r = new Rectangle();
5 r.width = 4;
6 r.height = 3;
7
8 int a = r.area();
9 System.out.println("Area is " + a);
10 }
11}
12
13class Rectangle {
14 int width;
15 int height;
16
17 int area() {
18 return width * height;
19 }
20}
Prints
›Area is 12

That prints Area is 12. Look closely at the method, because every method you ever write has the same four parts.

The four parts of a method

Reading the line int area() from left to right:

  • Return type (int): the type of value this method hands back to whoever called it. Here it gives back a whole number.
  • Name (area): what you call it by. Lowercase first letter, by convention, and usually a verb or a noun that reads like an action.
  • Parameters (the empty ()): the values the method needs handed in. This one needs nothing extra, so the parentheses are empty. We add some shortly.
  • Body (the { ... }): the code that runs when the method is called.
Key idea

A method is a named action that belongs to a class. It has a return type, a name, a list of parameters, and a body. Every method, including main, fits this same shape.

Coming from Python or C?
A Python def area(self): inside a class and a C function int area(...) do the same job as a Java method. The difference is where it lives: every Java method belongs to a class, glued to the data it works on. Java has no free-standing functions the way C does. Even the void main() of a compact source file belongs to a class that Java supplies for you.

Calling a method, and the object it runs on#

You call a method with the dot, the same dot you used to reach a field. r.area()reads as “run the area action belonging to r”. The parentheses are how you say “do it now”.

Now notice something inside the method body. It says width * height, not r.width * r.height. There is no r in sight. How does it know whose width to use? When you call r.area(), the method runs on r, so a bare widthinside the body means “the width of the object I was called on”. The object is already talking about itself.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 Rectangle small = new Rectangle();
5 small.width = 2;
6 small.height = 2;
7
8 Rectangle big = new Rectangle();
9 big.width = 10;
10 big.height = 5;
11
12 System.out.println("small area: " + small.area());
13 System.out.println("big area: " + big.area());
14 }
15}
16
17class Rectangle {
18 int width;
19 int height;
20
21 int area() {
22 return width * height;
23 }
24}
Prints
›small area: 4
›big area: 50

One method, written once. Called on small it uses small's fields and gives 4. Called on big it uses big's fields and gives 50. The body is shared, but each call works on its own object. This prints small area: 4 then big area: 50.

The name for ‘the object I was called on’
Java has an actual keyword for that object, this, and it matters when names collide. You do not need it yet, because nothing here is ambiguous. We give it a full lesson soon: the this keyword and object references. For now, “a bare field name means my own field” is the right picture.

Methods that change the object#

area only reads the fields. A method can also change them. Here is a Counter whose whole job is to hold a number and bump it up by one. The bumping is an action: increment. Press Run, step through, and watch the count field inside the object in the memory view. It is the same object both times; the number changes underneath it.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 Counter c = new Counter();
5
6 c.increment();
7 c.increment();
8
9 System.out.println("Count is " + c.count);
10 }
11}
12
13class Counter {
14 int count;
15
16 void increment() {
17 count = count + 1;
18 }
19}
Prints
›Count is 2

This prints Count is 2. The field count starts at 0 (an int field that nobody has set is 0, the default you met in creating classes and objects). The first increment() makes it 1, the second makes it 2. The line count = count + 1reads the object's own count, adds one, and stores it back into that same field.

But look at the front of increment. It does not say int, it says void. That is the thing to learn next.

void means “I hand nothing back”

void is the return type you write when a method does its job but has no value to give you. increment changes the counter; there is no number to return. area, by contrast, exists precisely to give you a number, so it returns an int. The return type is your promise about what comes back out.

A void method
A method that returns a value
Return type is void.
Return type is a real type: int, double, String, and so on.
Does something. Hands nothing back.
Computes something. Hands one value back.
Called as a statement on its own line: c.increment();
Called where a value is needed: int a = r.area();
The body may have a bare return; to stop early, or none at all.
The body must return a value of the right type on every path out.
Example: increment, print, deposit.
Example: area, getBalance, toUpperCase.
Quick check
increment is declared void increment(). What happens with the line int a = c.increment();?

Parameters: passing values in#

So far increment always adds exactly one. What if we want to add any amount? We let the caller hand a number in. A value handed into a method is passed through a parameter: a variable, declared in the parentheses, that exists only for the length of that one call.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 Counter c = new Counter();
5
6 c.add(5);
7 c.add(3);
8
9 System.out.println("Count is " + c.count);
10 }
11}
12
13class Counter {
14 int count;
15
16 void add(int amount) {
17 count = count + amount;
18 }
19}
Prints
›Count is 8

This prints Count is 8. The method now declares one parameter, int amount. When you write c.add(5), the value 5 you put in the parentheses is the argument, and it gets copied into the parameter amount for that call. So the body runs with amount equal to 5, making count 5. The next call copies 3 in, making count 8.

Two words, one idea, kept straight
The thing in the method's declaration is the parameter (int amount): a named slot waiting for a value. The thing you actually pass at the call is the argument (5): the real value. Parameter is the slot, argument is what goes in it. People mix the words up constantly; you do not have to.

A method can take several parameters, separated by commas, each with its own type. The arguments fill them in order, left to right.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 Rectangle r = new Rectangle();
5 r.setSize(6, 4);
6 System.out.println("Area is " + r.area());
7 }
8}
9
10class Rectangle {
11 int width;
12 int height;
13
14 void setSize(int w, int h) {
15 width = w;
16 height = h;
17 }
18
19 int area() {
20 return width * height;
21 }
22}
Prints
›Area is 24

This prints Area is 24. The call r.setSize(6, 4) copies 6 into w and 4 into h, by position, and the body stores them into the object's fields. Order matters: setSize(4, 6) would set a different rectangle.

Quick check
A Thermometer class needs a method that takes a temperature in Celsius and hands back the same temperature in Fahrenheit. Which first line fits?

return: passing a value back out#

Parameters carry values in. return carries one value out. When a method hits a return, two things happen at once: the method stops right there, and the value after return takes the place of the call. So wherever you write r.area(), the call becomes the number 12.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 Greeter g = new Greeter();
5
6 String message = g.greeting("Sana");
7 System.out.println(message);
8 System.out.println(g.greeting("Ravi"));
9 }
10}
11
12class Greeter {
13 String greeting(String name) {
14 return "Hello, " + name;
15 }
16}
Prints
›Hello, Sana
›Hello, Ravi

This prints Hello, Sana then Hello, Ravi. The method takes a String parameter and returns a String. The first call's result is stored in message and printed. The second call is dropped straight into println without a variable at all, because a method call that returns a value can be used anywhere that value would be allowed.

“The method stops right there” matters as soon as a method has more than one return. Decide what this prints before you run it.

Predict the outputGrades.java
1public class Main {
2
3 public static void main(String[] args) {
4 Grader g = new Grader();
5
6 System.out.println(g.grade(95));
7 System.out.println(g.grade(72));
8 System.out.println(g.grade(40));
9 }
10}
11
12class Grader {
13 String grade(int score) {
14 if (score >= 90) {
15 return "A";
16 }
17 if (score >= 70) {
18 return "B";
19 }
20 return "C";
21 }
22}

A score of 95 passes both tests, score >= 90 and score >= 70. What do the three lines print?

It prints A, B and C, one per line. For 95, the first test is true, so return "A" runs and the method is over: the second test is never even checked. For 72, the first test fails, the second passes, and the method returns "B". Only 40 gets all the way down to return "C". Java also checks that every path through the method ends in a return; delete the last line and javac reports missing return statement.

Key idea

Parameters pass values into a method. return passes one value back out, and the call stands in for that value. void is the special case of passing nothing back.

Put the lines in orderConvert.java
Put the lines in order so the program converts 25 degrees Celsius and prints 77.0. A method must work out its result before it can return it.

Tap a line, then tap where it should go. Or use the arrows.

ExerciseWallet.java
Finish the two methods of Wallet. add puts more coins in the wallet and hands nothing back. canAfford hands back true when the wallet holds at least price coins, and false otherwise. Leave main as it is.

Pass by value: Java copies the argument#

Here is a question that trips up almost everyone at first. If I pass a variable into a method, and the method changes its parameter, does my original variable change too? Run this and watch carefully.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 int score = 10;
5
6 Tweaker t = new Tweaker();
7 t.tryToChange(score);
8
9 System.out.println("score is still " + score);
10 }
11}
12
13class Tweaker {
14 void tryToChange(int number) {
15 number = number + 100;
16 System.out.println("inside the method, number is " + number);
17 }
18}
Prints
›inside the method, number is 110
›score is still 10

Read the output. Inside the method, number becomes 110. But back in main, scoreis still 10. The method's change did nothing to the caller's variable. The two lines printed are inside the method, number is 110 and then score is still 10.

The reason is the rule Java follows everywhere: pass by value. When you pass score as an argument, Java does not hand the method your variable. It copies the value (10) into the parameter number. From that point on, number is its own separate variable that happens to start at 10. Adding 100 changes the copy. Your original is untouched.

If you want the new value, you must return it
Because the caller's variable is never touched, a method like tryToChange is pointless as written. The fix is to return number; and have the caller write score = t.tryToChange(score);. For a value like an int, returning it is how a computed value gets back to where it is needed.
ExerciseDiscount.java
The shop takes 25% off, but the customer still pays 80.0, because applyDiscount only changes its own copy of the price. Change the method so it returns the new price, and change main so that price takes the result. The program should print You pay 60.0.

The one twist: objects

Pass by value is the whole rule, but there is a subtlety worth a careful sentence. When you pass an objectto a method, the value that gets copied is the reference, the object's address, not the object itself. Both the caller and the method then point at the same object. So the method cannot make your variable point somewhere new, but it canreach through the shared reference and change the object's fields.

Main.java
1public class Main {
2
3 public static void main(String[] args) {
4 Counter c = new Counter();
5
6 Bumper b = new Bumper();
7 b.bumpTwice(c);
8
9 System.out.println("count is " + c.count);
10 }
11}
12
13class Counter {
14 int count;
15
16 void increment() {
17 count = count + 1;
18 }
19}
20
21class Bumper {
22 void bumpTwice(Counter target) {
23 target.increment();
24 target.increment();
25 }
26}
Prints
›count is 2

This prints count is 2. The Counter we made in main and the target inside bumpTwice are two variables pointing at one and the same counter object, so bumping through target shows up when we read c.count afterwards. It is still pass by value: the reference got copied, and a copy of an address still points at the original object.

Now the other half of that sentence: the method cannot make your variable point somewhere new. This method changes a field through its parameter, then points the parameter at a brand-new counter. Decide what main prints.

Predict the outputReset.java
1public class Main {
2
3 public static void main(String[] args) {
4 Counter c = new Counter();
5 c.count = 5;
6
7 Resetter r = new Resetter();
8 r.reset(c);
9
10 System.out.println(c.count);
11 }
12}
13
14class Counter {
15 int count;
16}
17
18class Resetter {
19 void reset(Counter target) {
20 target.count = 0;
21 target = new Counter();
22 target.count = 99;
23 }
24}

After r.reset(c), what does c.count print?

It prints 0. The first line of reset reaches through the copied address into c's counter and sets its count to 0, and that change sticks. The next line only changes the method's own copy of the address: target now points at a new counter, and c in main knows nothing about it. The 99 goes into that new counter, which nothing points at once the method ends. Step through it and you will see the second Counter appear in the memory view, with c still pointing at the first.

This deserves a slow walk, later
Why two variables can share one object, and what is really copied, is exactly the subject of object references. For now hold two facts: simple values like intare copied, so a method cannot change your variable, while objects are shared, so a method can change the object's fields.

Overloading: same name, different parameters#

Sometimes one action makes sense in more than one form. A rectangle's area takes a width and a height; a square's area takes only a side. Both are rightly called “area”. Java lets you give two methods the same name as long as their parameter lists differ. This is called overloading, and Java picks the right one by looking at the arguments you pass.

Areas.java
1public class Main {
2
3 public static void main(String[] args) {
4 Areas calc = new Areas();
5
6 System.out.println(calc.area(3, 4));
7 System.out.println(calc.area(5));
8 }
9}
10
11class Areas {
12
13 int area(int width, int height) {
14 return width * height;
15 }
16
17 int area(int side) {
18 return side * side;
19 }
20}
Prints
›12
›25

There are two methods named area. The call calc.area(3, 4) has two arguments, so Java chooses the two-parameter version and returns 12. The call calc.area(5) has one argument, so Java chooses the one-parameter version and returns 25. Same name, the argument list decides. The output is 12 then 25. Step through it and watch which area each call jumps into.

Quick check
Which pair of methods would javac refuse to put in the same class?
Overloading is not overriding
These two words look alike and mean different things. Overloading is what we just did: several methods, same name, different parameters, in one class. Overriding is a separate idea that needs inheritance, replacing an inherited method with a new body. We meet overriding much later in method overriding and super. For now, only overloading.

Try it

Answer each one in your head or on paper first, then open the answer.

1
Name the four parts of the method int area(), and say what each one is.
2
When should a method's return type be void rather than int or String?
3
In c.add(5), which word is the parameter and which is the argument?
4
Trace the pass by value demo. After t.tryToChange(score) runs, what does score print, and why?
5
A class has int area(int side) and int area(int width, int height). Which one runs for calc.area(7), and what does it return?

Practice

Work through these on your own. Each one builds on this page.

1
Add a method int perimeter() to the Rectangle class (it returns 2 * (width + height)). In main, set a rectangle to width 6 and height 4, then print both its area and its perimeter. Confirm you get 24 and 20.
2
Give Counter two methods: void reset() that sets count back to 0, and int get() that returns the current count. Make a counter, add some amounts, print get(), then reset(), then print get() again. One method changes the object and returns nothing; the other only reads and returns a value.
3
Write a method int max(int a, int b) on a small Math2 class that returns the larger of its two parameters using an if-else. Then overload it: add a second method, also named max, that takes three int parameters and returns the largest of the three. Call both from main and check the results by hand.
4
In one short paragraph in your own words, explain why a method that takes an int parameter cannot change the caller's variable, but a method that takes a Counter parameter can change that counter's fields. Use the words copy and reference.

Further reading#

Short guides on this topic

Quick answers to what people search for about it, with code to run.

Spotted a mistake, or a part that did not make sense? Report it by email