Module 4 · Methods & Modularity ⏱ 19 min

Methods

By the end of this lesson you will be able to:
  • Declare a method with parameters and a return type, and a void method that performs an action
  • Call a method and use the value it returns
  • Explain overloading — two methods with the same name but different parameter types
  • Predict the effect of Java's pass-by-value on a primitive argument

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
Calling a method: control jumps into the body, the body runs, and a return sends a value back to the caller.

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.

Main.java — a value-returning method, a void method, and an overloaded pair. Real, compiling Java.
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
Overload resolution: the compiler picks the one overload whose parameter type matches the argument.

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
Pass-by-value: a call copies the value into the parameter. Changing the copy never reaches the original.
Main.java — addOne changes its own copy, so the caller's score is unchanged. Real, compiling Java.
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.

Main.java — a void method uses a bare return; to exit early and print nothing. Real, compiling Java.
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.

Exercise

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");
    }
}
Exercise

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);
    }
}
Exercise

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));
    }
}
Exercise

Look at square(6) called against the definition static int square(int n). Which statement is correct?

Exercise

What does it mean when a method's return type is void?

Recap

  • A method signature gives the return type, name, parameters, and body; void means 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.
  • return exits immediately; in a void method a bare return; bails out early as a guard clause.
  • Use return to compute, println to display — they are not interchangeable.

Next you will put methods and fields together inside a class and start building your own objects.

Checkpoint quiz

Two methods with the same name but different parameter types is called…

A method changes the value of its int parameter, but the caller's variable is unchanged afterward. Why?

Go deeper — technical resources