Alexander Claes
← Learning Java

Introduction to Methods

A practical introduction to Java methods: static and instance methods, parameters, return values, overloading, and variable scope, with examples for JavaScript developers.

Alexander Claes Alexander Claes 5 min read
Learning Java
Two teal inputs connect to a glass module marked with curly braces, with an amber result on the other side.

Once you have a few variables and calculations in place, putting everything inside main starts to get messy. Methods let us give a piece of work a name and call it whenever we need it. Instead of repeating the same score calculation, we can write it once and pass in different values.

If you’re coming from JavaScript, the idea will feel familiar: inputs go in, code runs, and a result can come back. Java asks us to be explicit about the types along the way.

Writing and calling a method

Let’s stick with the player scores from the previous article about variables and types. Save this complete example as MethodsDemo.java:

public class MethodsDemo {
    static int addBonus(int score, int bonus) {
        return score + bonus;
    }

    public static void main(String[] args) {
        int totalScore = addBonus(20, 5);
        System.out.println(totalScore); // 25
    }
}

Compile it with javac MethodsDemo.java, then run it with java MethodsDemo. The output is 25. Notice that addBonus sits inside the class, alongside main. We don’t declare it inside main like a nested JavaScript function.

The int before addBonus is its return type. The two values inside the parentheses, int score and int bonus, are its parameters. When we call addBonus(20, 5), 20 and 5 are the arguments supplied to those parameters.

return sends the calculated value back to the caller and exits the method. Here, we store that value in totalScore. Printing a value and returning it are separate things: addBonus calculates the score; println displays it.

Static and instance methods

A static method belongs to the class. We can call it without creating an object, using MethodsDemo.addBonus(20, 5). Inside the same class, the shorter addBonus(20, 5) works too. That’s handy for a calculation whose inputs arrive as arguments.

A non-static method, usually called an instance method, runs on a particular object. Here’s a separate example to save as Player.java:

public class Player {
    private int score = 20;

    public void addBonus(int bonus) {
        score += bonus;
    }

    public int getScore() {
        return score;
    }

    public static void main(String[] args) {
        Player alex = new Player();
        Player sam = new Player();

        alex.addBonus(5);

        System.out.println(alex.getScore()); // 25
        System.out.println(sam.getScore());  // 20
    }
}

Each player has its own score field. Calling alex.addBonus(5) changes Alex’s score; Sam’s stays the same. public makes these methods accessible to callers outside the class, while private keeps the score field behind the class’s methods.

So static methods aren’t simply Java’s name for JavaScript functions. JavaScript classes have static and instance methods too. The useful distinction is whether the call needs a particular object. A static method has no current instance, so it can’t use this or directly access an instance field. It can still work with an object it receives or creates, as main does here. See the Java specification’s explanation of static methods for the precise rule.

When there is nothing to return

The player’s addBonus method uses void because it changes the player’s state without returning a value. getScore, on the other hand, returns an int. Choose the return type according to what the caller should receive.

You can’t assign the result of alex.addBonus(5) to an int: there is no returned value to store. A void method can finish by reaching its closing brace, or exit early with return;. A method that promises a value must return a compatible value on every path that completes normally.

Overloading: one name, different inputs

Now imagine that our usual bonus is five points, but we sometimes need a custom bonus. Add this method alongside the existing two-parameter addBonus in MethodsDemo:

static int addBonus(int score) {
    return addBonus(score, 5);
}

We can now make either call inside main:

System.out.println(addBonus(20));    // 25
System.out.println(addBonus(20, 10)); // 30

This is overloading: methods share a name but have different parameter lists. Here, Java picks the matching method from the number of arguments. Overloads can also differ in parameter types. Changing only parameter names or the return type won’t create a valid overload. The official guide to defining methods covers these rules.

The one-parameter version delegates to the two-parameter version, so the addition stays in one place. This also gives us a convenient default without JavaScript-style default parameter syntax. Keep overloads doing the same kind of work; a familiar name is only helpful if its behaviour stays predictable.

Managing the scope of variables

A local variable declared in a method block is available from its declaration through the rest of that block, including nested blocks. Method parameters are available throughout the method body. Neither becomes accessible in the caller just because the method ran.

Add this method to MethodsDemo and call printScore(20) from main:

static void printScore(int score) {
    int totalScore = score;

    if (score >= 20) {
        int bonus = 5;
        totalScore += bonus;
    }

    System.out.println(totalScore); // 25 when score is 20
    // System.out.println(bonus);   // Would not compile here
}

bonus only exists as a usable name inside the if block. totalScore is declared in the surrounding method block, so we can update it inside the if and read it afterwards. The totalScore in main is a separate variable, even though the names match.

One difference from JavaScript’s let: you can’t redeclare an enclosing local variable or method parameter with the same name in an ordinary nested block. Keep names distinct where their scopes overlap. These boundaries follow Java’s scope rules.

Passing a value doesn’t hand over the variable

There’s one more detail behind those method calls: Java passes arguments by value. For an int, the parameter gets a copy of the number. Add this method to MethodsDemo:

static void tryToAddBonus(int score) {
    score += 5;
}

Then try this inside main:

int playerScore = 20;
tryToAddBonus(playerScore);
System.out.println(playerScore); // Still 20

playerScore = addBonus(playerScore, 5);
System.out.println(playerScore); // 25

Changing the parameter doesn’t change the caller’s variable. Returning the new score and assigning it does. Objects follow the same pass-by-value rule, but the copied value is a reference: a method can mutate the referenced object, while reassigning its parameter won’t replace the caller’s reference. The official guide to method arguments walks through both cases.

For practice, add a doubleScore(int score) method to MethodsDemo. Return twice the input, call it from main, and store the result. Then give it an overload with no parameters that calls doubleScore(20). It’s a small exercise, but it puts parameters, return values, and overloading to work together.