A method is a named chunk of code you can run from elsewhere. Instead of copying the same ten lines into every corner of your program, you write them once, give them a name, and call that name whenever the work needs doing. main itself is just a method — the one the JVM happens to call first. Methods buy you reuse, a readable name where twelve lines of arithmetic sat before, and isolation: a method's own variables cannot quietly corrupt the code that called it.
The shape of a method
A Java method signature tells you four things: the return type (what kind of value comes back), the name, the parameters in parentheses — each one a type plus a name — and the body in braces. A method that produces no value is declared void. If you declare a return type of int, every exit path must hand back an int; returning a String from it, or forgetting to return at all, fails the build. Every example here carries the word static so the main method can call it directly without first building an object — you will drop that word in the next lesson, when methods start living on objects.
int square(int n) { // takes an int, returns an int
return n * n;
}
void greet(String name) { // takes a String, returns nothing
System.out.println("Hi, " + name);
}
flowchart LR C["main calls square(6)"] --> M["square body runs"] M --> R["return 36"] R --> P["caller receives 36"] style C fill:#c2410c,color:#fff style R fill:#c2410c,color:#fff
Parameters and arguments
Two words that beginners treat as synonyms and that the compiler treats as different things. The list inside the definition — int n in square(int n) — names the parameters: placeholders that exist only while the method runs. The actual values you hand over in a call — the 6 in square(6) — are the arguments. Keeping that distinction straight makes later error messages readable: a complaint about actual and formal argument lists differing in length is just the compiler saying you passed the wrong number of arguments for the parameters it expected.
public class Main {
static int square(int n) {
return n * n;
}
static double square(double n) {
return n * n;
}
static void greet(String name) {
System.out.println("Hi, " + name);
}
public static void main(String[] args) {
System.out.println(square(6));
System.out.println(square(2.5));
greet("Sam");
}
}
Overloading — same name, different inputs
Java lets several methods share one name as long as their parameter lists differ. The compiler reads the arguments at each call site and picks the single matching overload. Calling square(6) resolves to the int version; calling square(2.5) resolves to the double version. When two overloads could both apply, the compiler chooses the most specific one, and if none is clearly closest it rejects the call as ambiguous. Two methods, one tidy name — that is overloading, and it is how System.out.println can accept an int, a String, or a double without you ever noticing.
flowchart TD
C["describe(5.0)"] --> Q{"matching overload?"}
Q --> A1["describe(int n)<br/>not applicable"]
Q --> A2["describe(double n)<br/>exact match — chosen"]
style Q fill:#3776ab,color:#fff
style A2 fill:#15803d,color:#fff
style A1 fill:#b91c1c,color:#fff
Pass-by-value: the method gets a copy
When you pass a primitive to a method, Java hands it a copy of the value, never the original variable. Inside the method you can reassign that copy as much as you like; the caller's variable is untouched. This is called pass-by-value, and it is the source of the most common beginner surprise: a method that 'adds one' to its parameter and yet leaves the caller's number exactly where it was. The cure is never to mutate the parameter — return the new value instead, and let the caller decide what to do with it. Reference types follow the same rule by value too, but what gets copied there is the reference rather than the object — a subtlety that belongs in the next lesson on objects.
flowchart LR
subgraph MAIN["in main"]
S["score = 10"]
end
subgraph ADD["in addOne, while it runs"]
X["x = 10 (a copy)"]
X2["x = 11 (copy changed)"]
end
S -->|"value copied"| X
X --> X2
X2 -.->|"score stays 10"| S
style S fill:#3776ab,color:#fff
style X2 fill:#b45309,color:#fff
public class Main {
static void addOne(int x) {
x = x + 1;
}
public static void main(String[] args) {
int score = 10;
addOne(score);
System.out.println(score); // still 10, not 11
}
}
return ends the method
The instant a return runs, the method stops and hands its value back — any code below it is skipped, and the compiler in fact treats code that can never run as an error. A void method has no value to return, but you can still write a bare return; to bail out early. That pattern — check a condition, return if it fails, otherwise continue — is called a guard clause, and it keeps the happy path un-indented and easy to read.
public class Main {
static void printPositive(int n) {
if (n < 0) {
return; // exit early, print nothing
}
System.out.println(n);
}
public static void main(String[] args) {
printPositive(7); // prints 7
printPositive(-3); // prints nothing
}
}
return versus System.out.println
These look alike in a tutorial and behave nothing alike. println writes text to the console for a human to read; return hands a value back to the calling code, which can store it, add to it, or pass it on. A method that prints its answer but forgets to return it looks correct on screen yet hands back nothing useful — and any caller that tries to use that result is working with emptiness. Compute with return; reach for println only at the outermost layer that actually faces the user.
What does this program print? Read it carefully and type the exact output (four lines).
public class Main {
static int square(int n) {
return n * n;
}
static double square(double n) {
return n * n;
}
static String repeat(String word, int times) {
String result = "";
for (int i = 0; i < times; i++) {
result = result + word;
}
return result;
}
static void greet(String name) {
System.out.println("Hi, " + name);
}
public static void main(String[] args) {
System.out.println(square(6));
System.out.println(square(2.5));
System.out.println(repeat("ab", 3));
greet("Sam");
}
}
square(6)uses theintversion:6 * 6 = 36.square(2.5)uses thedoubleversion:2.5 * 2.5 = 6.25.repeat("ab", 3)joins"ab"three times:"ababab".
Pass-by-value check. A method tries to double its parameter. Predict the single line printed by main.
public class Main {
static void doubleIt(int n) {
n = n * 2;
}
public static void main(String[] args) {
int value = 8;
doubleIt(value);
System.out.println(value);
}
}
doubleItreceives a copy ofvalue, notvalueitself.Reassigning the parameter inside the method cannot reach the caller's variable.
Overload resolution. Two methods share the name describe. Predict both lines — think about which overload each call picks.
public class Main {
static String describe(int n) {
return "int " + n;
}
static String describe(double n) {
return "double " + n;
}
public static void main(String[] args) {
System.out.println(describe(5));
System.out.println(describe(5.0));
}
}
5is anintliteral, so it matchesdescribe(int n).5.0is adoubleliteral, so it matchesdescribe(double n).
Look at square(6) called against the definition static int square(int n). Which statement is correct?
Parameters live in the definition (here int n); arguments are the concrete values supplied at the call site (here 6).
What does it mean when a method's return type is void?
void means 'nothing comes back'. You call such a method for what it does (like printing), not for a return value — so there's no return someValue; inside.
Recap
- A method signature gives the return type, name, parameters, and body;
voidmeans nothing comes back. - Parameters are the placeholders in the definition; arguments are the values you pass at a call site.
- Overloading reuses one method name across different parameter lists, and the compiler picks the matching one.
- Java is pass-by-value: a method gets a copy, so changing its parameter never reaches the caller's variable.
returnexits immediately; in avoidmethod a barereturn;bails out early as a guard clause.- Use
returnto compute,printlnto display — they are not interchangeable.
Next you will put methods and fields together inside a class and start building your own objects.