Skip to content
Java oop basics 7 min read

Methods

Methods are the building blocks of every Java program. They let you name a reusable chunk of logic, call it whenever you need it, and keep your code clean and readable — whether you’re writing a ten-line script or a million-line enterprise app.

What Is a Method?

A method is a named block of code inside a class that performs a specific task. Instead of copy-pasting the same logic everywhere, you write it once in a method and call it by name.

public class Greeter {
    // declaring a method
    static void sayHello() {
        System.out.println("Hello, World!");
    }

    public static void main(String[] args) {
        sayHello(); // calling the method
        sayHello(); // call it as many times as you want
    }
}

Output:

Hello, World!
Hello, World!

Method Syntax

<access-modifier> <other-modifiers> <return-type> <methodName>(<parameters>) {
    // body
    return value; // only needed when return-type is not void
}
PartWhat it means
access modifierpublic, private, protected, or package-private
other modifiersstatic, final, abstract, synchronized, etc.
return typethe type of value the method gives back; void means nothing
method namecamelCase identifier (e.g., calculateTax)
parameterscomma-separated list of typed variables the caller passes in
bodythe actual code that runs

Tip: Follow Java naming conventions — method names start with a lowercase letter and use camelCase: getUserName(), calculateTotal().

Parameters and Arguments

Parameters are the variables listed in the method declaration. Arguments are the actual values you pass when calling the method.

public class Calculator {
    // two parameters: a and b
    static int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = add(3, 7); // 3 and 7 are arguments
        System.out.println("Sum: " + result);
    }
}

Output:

Sum: 10

Passing Multiple Parameters

static String buildGreeting(String name, int age) {
    return "Hi " + name + ", you are " + age + " years old.";
}

Note: Java always passes primitives by value — the method gets a copy of the number, not a reference to the original variable. For objects, the reference is passed by value, meaning you can mutate the object’s fields but cannot reassign the caller’s variable. See Call by Value for a deeper dive.

Return Types

A method can return any type — a primitive, an object, an array, or nothing at all (void).

public class ShapeUtils {

    // returns a double
    static double circleArea(double radius) {
        return Math.PI * radius * radius;
    }

    // returns a String
    static String classify(int n) {
        return (n % 2 == 0) ? "even" : "odd";
    }

    // returns void — nothing to return
    static void printDivider() {
        System.out.println("----------");
    }

    public static void main(String[] args) {
        System.out.println(circleArea(5.0));
        System.out.println(classify(7));
        printDivider();
    }
}

Output:

78.53981633974483
odd
----------

Warning: If your return type is anything other than void, the compiler enforces that every code path returns a compatible value. Forgetting a return in a branch causes a compile-time error.

Static vs Instance Methods

Static methodInstance method
Belongs toThe classA specific object
Call syntaxClassName.method()objectRef.method()
AccessesOnly static fields/methodsBoth static and instance fields/methods
Common useUtility / factory methodsBusiness logic that needs object state
public class Counter {
    private int count = 0; // instance field

    // instance method — needs the object's state
    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }

    // static utility — needs no object state
    public static int add(int x, int y) {
        return x + y;
    }

    public static void main(String[] args) {
        Counter c = new Counter();
        c.increment();
        c.increment();
        System.out.println(c.getCount());          // 2
        System.out.println(Counter.add(10, 20));   // 30
    }
}

Output:

2
30

For more on the static keyword, see static Keyword.

Method Overloading

Java lets you define several methods with the same name as long as their parameter lists differ (different number, types, or order of parameters). The compiler picks the right one at compile time — this is called compile-time polymorphism.

public class Printer {

    static void print(int n) {
        System.out.println("int: " + n);
    }

    static void print(double d) {
        System.out.println("double: " + d);
    }

    static void print(String s) {
        System.out.println("String: " + s);
    }

    static void print(int a, int b) {
        System.out.println("two ints: " + a + ", " + b);
    }

    public static void main(String[] args) {
        print(42);
        print(3.14);
        print("hello");
        print(1, 2);
    }
}

Output:

int: 42
double: 3.14
String: hello
two ints: 1, 2

Note: Return type alone does not distinguish overloaded methods. int foo() and void foo() in the same class is a compile error.

Dive deeper at Method Overloading and Compile-Time Polymorphism.

Variable-Length Arguments (Varargs)

When you don’t know how many arguments the caller will pass, use a varargs parameter with .... Java bundles the values into an array for you.

static int sum(int... numbers) {
    int total = 0;
    for (int n : numbers) {
        total += n;
    }
    return total;
}

public static void main(String[] args) {
    System.out.println(sum(1, 2, 3));        // 6
    System.out.println(sum(10, 20, 30, 40)); // 100
    System.out.println(sum());               // 0
}

Output:

6
100
0

Tip: Varargs must be the last parameter in the list. Learn more at Varargs.

Recursive Methods

A method can call itself — this is called recursion. Each call adds a new frame to the call stack until the base case is reached.

static int factorial(int n) {
    if (n <= 1) return 1;         // base case
    return n * factorial(n - 1);  // recursive call
}

public static void main(String[] args) {
    System.out.println(factorial(5)); // 120
}

Output:

120

Warning: Infinite recursion causes a StackOverflowError. Always define a clear base case. See Recursion for a full guide.

Under the Hood

When you invoke a method, the JVM pushes a new stack frame onto the current thread’s Java stack. The frame holds:

  • The local variable array — slots for all parameters and local variables.
  • The operand stack — a working area for intermediate computation results.
  • A reference back to the runtime constant pool of the class.

When the method returns, its frame is popped and control resumes in the calling frame.

Static dispatch vs dynamic dispatch

  • A call to a static or private method is resolved at compile time (stored as a invokestatic or invokespecial bytecode instruction). The JVM jumps straight to the target.
  • A call to a non-final instance method uses invokevirtual, which looks up the method in the object’s vtable at runtime — enabling runtime polymorphism.

JIT inlining

The JIT compiler watches which methods are called frequently (“hot methods”). Short methods — especially getters, setters, and small utilities — are often inlined: the callee’s bytecode is substituted directly into the caller, eliminating the frame-creation overhead entirely. This is why small, focused methods are not only more readable but also perform well in production. See JIT Compilation for the full picture.

Stack depth and recursion

The default thread stack size is typically 256 KB–1 MB (JVM-dependent). Each frame consumes memory for its locals and operand stack, so deeply recursive methods can exhaust the stack. For deep recursion, consider converting to an iterative approach or increasing the stack size with -Xss.

Common Mistakes

  • Forgetting return — if every code path must return a value, the compiler catches it, but conditional branches are easy to miss.
  • Returning the wrong type — a widening conversion (e.g., intlong) is fine; a narrowing one requires an explicit cast.
  • Overloading on return type only — Java does not support this; you must differ on parameters.
  • Shadowing fields with local variables — inside an instance method, a local variable named count hides the field this.count. Use this.count to refer to the field explicitly.
Last updated June 13, 2026
Was this helpful?