Java · Beginner
Methods in Java
In short: A method is a named block of code that takes parameters, does one job and optionally returns a value: static int area(int w, int h) { return w * h; }. Calling it copies the argument values into the parameters and runs the body; Java always passes arguments by value.
Naming a piece of work
Once a program does the same calculation in two places, or main grows past a screen, the code needs to be split into named pieces. A method is such a piece: it has a name, takes zero or more inputs called parameters, and may hand back a result. Calling the method by name runs its body. The benefits are the ones every good name gives: the call site reads as a sentence, the logic lives in one place, and that one place can be tested on its own.
A method declaration has a fixed order of parts. First come modifiers; the methods in this lesson are static, which means they belong to the class and can be called straight from main without creating an object. Then the return type, which is void when the method returns nothing. Then the name, by convention a lowerCamelCase verb phrase such as parcelCost or isValidPin. Then the parameter list in parentheses, each parameter written as a type and a name. The body in braces follows.
The return statement ends the method immediately and, for a non-void method, supplies the value. A method with a return type must return on every possible path; the compiler rejects one that can fall off the end without returning, with missing return statement. Several return statements are fine and often clearest: a validation method can return false as soon as it finds a problem and return true at the end, which is called an early return. In a void method a bare return; exits early.
When the method is called, each argument is evaluated and its value is copied into the corresponding parameter. This is pass by value, and Java has no other mode. For a primitive, the method gets a copy of the number and cannot change the caller's variable. For an array or other object, the value copied is the reference, so the method can modify the elements of the caller's array, but reassigning the parameter to a new array affects only the copy. Both behaviours follow from one rule: the parameter is a new variable initialised with the argument's value.
A class may hold several methods with the same name as long as their parameter lists differ in number or types. This is overloading; the compiler picks the version whose parameters match the arguments. Overloading by return type alone is not allowed, because the compiler could not tell the calls apart. A three-argument largest that calls the two-argument one twice is the typical use.
Variables declared inside a method, including its parameters, are local: they come into existence when the method is called and vanish when it returns. Two methods may both use a variable named total without interfering, and main cannot see a variable declared inside parcelCost. Inputs travel in through parameters and results out through the return value, which is what makes a method reusable.
A method may call itself, which is recursion; the recursion lesson covers when that is the right tool. Methods that belong to objects rather than to the class, written without static, are the subject of the classes and objects lesson.
Syntax
static returnType name(type param1, type param2) {
// body; may use param1 and param2
return value; // required unless returnType is void
}
static void name(type param) {
// no result; a bare return; exits early
}
result = name(arg1, arg2); // call: arguments are copied into the parameters
name(arg); // call a void method as a statementThe method's signature is its name plus the parameter types. Overloads must differ in signature; the return type is not part of it.
Defining and calling methods
A courier's price rule written once and used three times, plus a void method that prints a label.
public class Main {
static double parcelCost(double weightKg) {
if (weightKg <= 1) {
return 4.0;
}
return 4.0 + (weightKg - 1) * 1.5;
}
static void printLabel(String city, double cost) {
System.out.println(city + ": " + cost);
}
public static void main(String[] args) {
printLabel("Leeds", parcelCost(0.5));
printLabel("Cork", parcelCost(3));
double total = parcelCost(2) + parcelCost(1);
System.out.println("Total: " + total);
}
}Output
Leeds: 4.0 Cork: 7.0 Total: 9.5
parcelCost takes a double and returns a double; the first return handles light parcels and exits before the second line runs. Passing the int literal 3 is fine because int widens to double. The result of one call becomes the argument of another in printLabel("Cork", parcelCost(3)). printLabel returns nothing, so it is called as a statement rather than inside an expression.
Early return and overloading
A PIN validator that stops at the first problem, and two methods that share a name. The program is run with the input 0473.
import java.util.Scanner;
public class Main {
static boolean isValidPin(String pin) {
if (pin.length() != 4) {
return false;
}
for (int i = 0; i < pin.length(); i++) {
if (!Character.isDigit(pin.charAt(i))) {
return false;
}
}
return true;
}
static int largest(int a, int b) {
return a > b ? a : b;
}
static int largest(int a, int b, int c) {
return largest(largest(a, b), c);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String pin = in.nextLine();
System.out.println(pin + " valid: " + isValidPin(pin));
System.out.println("7a31 valid: " + isValidPin("7a31"));
System.out.println(largest(4, 9));
System.out.println(largest(4, 9, 2));
}
}Input given to the program: 0473
Output
0473 valid: true 7a31 valid: false 9 9
isValidPin returns false the moment it sees a wrong length or a non-digit, so the loop never needs a flag variable; reaching the final return true means every check passed. The two largest methods have different parameter counts, so largest(4, 9) and largest(4, 9, 2) each pick the matching one, and the three-argument version reuses the two-argument one instead of repeating the comparison.
Arguments are passed by value
One method tries to change an int, the other changes an element of an array. Only one of them affects the caller.
public class Main {
static void tryToChange(int n) {
n = n + 100;
System.out.println("inside: " + n);
}
static void markSold(int[] stock, int index) {
stock[index] = 0;
}
public static void main(String[] args) {
int count = 5;
tryToChange(count);
System.out.println("after: " + count);
int[] shelf = {3, 8, 2};
markSold(shelf, 1);
System.out.println(shelf[0] + " " + shelf[1] + " " + shelf[2]);
}
}Output
inside: 105 after: 5 3 0 2
n is a copy of count; adding 100 changes the copy, and count is still 5 afterwards. stock is a copy of the reference to the same array shelf points at, so writing stock[1] = 0 changes the shared array and main sees the zero. Had markSold written stock = new int[3] instead, only its own copy of the reference would have changed.
What a method receives
| Argument type | The parameter holds | Can the method change the caller's data? |
|---|---|---|
primitive (int, double, boolean, ...) | a copy of the value | no |
| array or object reference | a copy of the reference | yes, through the reference: elements and fields |
| any | a new local variable | reassigning the parameter never affects the caller |
Common mistakes
A path with no return
Why it goes wrong:
static int sign(int n) { if (n > 0) { return 1; } }fails withmissing return statement: when n is not positive the method would end without a value.Fix: Return on every path, typically by ending with an unconditional return.
Java · fixstatic int sign(int n) { if (n > 0) { return 1; } return 0; }Calling a non-static method from main
Why it goes wrong: A method declared without
staticbelongs to an object. Calling it from main givesnon-static method greet() cannot be referenced from a static context, because no object exists yet.Fix: Mark helper methods
staticwhile everything lives in Main; create an object first once you move to classes.Java · fixstatic void greet() { System.out.println("hello"); }Expecting a method to change a primitive argument
Why it goes wrong:
static void doubleIt(int n) { n *= 2; }changes only its local copy; the caller's variable keeps its value.Fix: Return the new value and assign it at the call site.
Java · fixstatic int doubled(int n) { return n * 2; } // caller: count = doubled(count);Overloading on the return type only
Why it goes wrong:
static int f(int a)andstatic double f(int a)together fail withmethod f(int) is already defined; the parameter lists are identical, so a callf(3)could not be resolved.Fix: Give the methods different parameter lists or different names.
Where you use this
A grading program reads a set of marks, computes their average and prints a grade letter. Written as one long main, the three jobs blur together and none can be checked separately. Split into readMarks, average and gradeFor, each method has one purpose, main reads as the outline of the program, and the grade rule can be tested by calling gradeFor(72) directly. Methods also remove duplication: a bug fixed in gradeFor is fixed for every caller at once.
public static void main(String[] args) {
int[] marks = readMarks();
double avg = average(marks);
System.out.println("Average " + avg + ", grade " + gradeFor(avg));
}
static String gradeFor(double avg) {
if (avg >= 70) {
return "A";
}
if (avg >= 50) {
return "B";
}
return "C";
}Key points
- A method is declared as modifiers, return type, name, parameter list and body;
voidmeans no result. returnends the method and supplies the value; every path of a non-void method must return.- Arguments are passed by value: primitives are copied, references are copied, so array elements can be changed but the caller's variable cannot be reassigned.
- Overloads share a name and differ in parameter types or count, never only in return type.
- Parameters and variables declared inside a method are local to that call.
- Helpers called from main must be
staticuntil you work with objects. - Name methods as verb phrases that say what they do:
parcelCost,isValidPin,printLabel.
Try it yourself
Complete the cube method so that it returns its argument multiplied by itself three times. The calls in main should then print 27 and 1000.
public class Main {
static int cube(int n) {
return 0; // replace this
}
public static void main(String[] args) {
System.out.println(cube(3));
System.out.println(cube(10));
}
}
27
1000public class Main {
static int cube(int n) {
return n * n * n;
}
public static void main(String[] args) {
System.out.println(cube(3));
System.out.println(cube(10));
}
}
Practise this
Exercises for this lesson are in the Java practice set.
Related lessons
- Arrays in JavaHow Java arrays store a fixed number of values of one type: creating and indexing them, length, the for-each loop, java.util.Arrays helpers and 2D arrays.9 min
- Strings in JavaHow Java strings work: immutability, the methods you use daily, why equals not == compares text, trimming and splitting input, StringBuilder and String.format.10 min
Frequently asked questions
Is Java pass by value or pass by reference?
Pass by value, always. The method's parameter is a fresh variable initialised with a copy of the argument. For a primitive that is a copy of the number. For an object the argument is a reference, so the copy points at the same object, which is why a method can change an array's elements; but assigning the parameter to a new object does not touch the caller's variable, which is the test that distinguishes it from true pass by reference.
Can a Java method return more than one value?
Not directly; a method has one return type. To hand back several values, return an array, a small object or a record (Java 16 and later, covered in the records lesson). Returning a record such as record Stats(int min, int max) {} is the clearest option because each value keeps its name.
How this page was checked. Every program on it was run with OpenJDK 21 at build time by the publishing checks, and the output shown is what it printed. Running Java inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with OpenJDK 21 locally.