Java: Methods & Parameter Passing

Nagesh Chauhan 26 Jun 2026 7 min read
0
Methods define the behavior of a class. They encapsulate reusable logic, improve code organization, reduce duplication, and provide well-defined interfaces between different parts of an application.

Every executable statement in Java is ultimately executed within a method.

Method Declaration

A method declaration consists of an optional access modifier, optional non-access modifiers, return type, method name, parameter list, optional throws clause, and method body.
public int add(int a, int b) {
    return a + b;
}
A method signature consists only of the method name and parameter types. The return type is not part of the method signature.

Method Components

A method may contain the following components.

- Access Modifier
- Non-Access Modifiers
- Return Type
- Method Name
- Parameter List
- Throws Clause
- Method Body
public static final int calculate(int value) throws Exception {
    return value * 2;
}

Access Modifiers

Methods can use one of the following access modifiers.

- public allows access from anywhere.
- protected allows access within the package and subclasses.
- Package-private (no modifier) allows access only within the package.
- private allows access only within the declaring class.
public void publicMethod() {}
protected void protectedMethod() {}
void packagePrivateMethod() {}
private void privateMethod() {}

Non-Access Modifiers

Common non-access modifiers include:

- static
- final
- abstract
- synchronized
- native
- strictfp

These modifiers change method behavior and are discussed in detail in their respective topics.

Return Type

A method may return a value or return nothing.
public int square(int number) {
    return number * number;
}

public void printMessage() {
    System.out.println("Hello");
}
Every execution path of a non-void method must return a compatible value.

Method Naming

Method names should clearly describe behavior and typically begin with a verb. Examples include:
calculateSalary()
findEmployee()
saveOrder()
sendEmail()
isValid()
Boolean-returning methods commonly begin with is, has, or can.

Parameters and Arguments

- Parameters are variables declared in the method definition.
- Arguments are values supplied during method invocation.
public void greet(String name) {
    System.out.println(name);
}

greet("John");
Here, name is the parameter and "John" is the argument.

Java is Always Pass-by-Value

Java always uses pass-by-value. For primitive types, the actual value is copied.
void increment(int value) {
    value++;
}

int number = 10;
increment(number);

System.out.println(number);
Output:
10
The original variable remains unchanged.

For objects, the reference value is copied, not the object.
class Employee {
    String name;
}

void change(Employee employee) {
    employee.name = "Alice";
}

Employee employee = new Employee();
employee.name = "John";

change(employee);
System.out.println(employee.name);
Output:
Alice
Both variables refer to the same object because the object reference was copied. Reassigning the copied reference does not affect the original variable.
void reset(Employee employee) {
    employee = new Employee();
}

Employee employee = new Employee();
reset(employee);
The caller still references the original object.

Local Variables

Variables declared inside a method exist only during method execution.
void display() {
    int number = 100;
    System.out.println(number);
}
Local variables must be initialized before use.

Variable Scope

The scope of a variable is limited to the block in which it is declared.
if (true) {
    int value = 100;
}
// value is not accessible here
Keeping variable scope as small as possible improves readability and reduces bugs.

Method Overloading

Methods may have the same name provided their parameter lists differ.
int add(int a, int b) {
    return a + b;
}
double add(double a, double b) {
    return a + b;
}
int add(int a, int b, int c) {
    return a + b + c;
}
Changing only the return type does not create a valid overload.

Varargs

Varargs allow a method to accept a variable number of arguments.
public int sum(int... numbers) {
    int total = 0;
    for (int number : numbers) {
        total += number;
    }
    return total;
}
Method invocation:
sum();
sum(10);
sum(10, 20, 30);
Internally, varargs are implemented as arrays. A method can declare only one varargs parameter, and it must be the last parameter.

Recursive Methods

A method can invoke itself.
int factorial(int number) {
    if (number == 1) {
        return 1;
    }
    return number * factorial(number - 1);
}
Every recursive method must define a termination condition to prevent infinite recursion. Deep recursion may result in StackOverflowError.

Static Methods

Static methods belong to the class rather than an object.
class MathUtil {
    static int square(int number) {
        return number * number;
    }
}
MathUtil.square(10);
Static methods cannot directly access instance variables or invoke instance methods.

Instance Methods

Instance methods operate on object state.
class Employee {
    String name;
    void display() {
        System.out.println(name);
    }
}
Instance methods require an object for invocation.

Method Hiding

Static methods are hidden, not overridden.
class Parent {
    static void display() {
        System.out.println("Parent");
    }
}

class Child extends Parent {
    static void display() {
        System.out.println("Child");
    }
}
The method executed depends on the reference type rather than the runtime object.

Method Overriding

A subclass may provide a different implementation for an inherited method.
class Animal {
    void sound() {
        System.out.println("Animal");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Bark");
    }
}
Runtime method selection is based on the actual object type.

Covariant Return Types

An overridden method may return a subtype of the original return type.
class Animal {
}
class Dog extends Animal {
}
class AnimalFactory {
    Animal create() {
        return new Animal();
    }
}
class DogFactory extends AnimalFactory {
    @Override
    Dog create() {
        return new Dog();
    }
}
Covariant return types improve type safety.

Method References

Java supports method references as a concise alternative to simple lambda expressions.
List<String> names = List.of("John", "Alice");
names.forEach(System.out::println);
Method references improve readability when the lambda only invokes an existing method.

Generic Methods

Methods may declare their own type parameters.
public <T> void print(T value) {
    System.out.println(value);
}
Generic methods provide compile-time type safety while avoiding unnecessary casting.

throws Clause

A method can declare checked exceptions using the throws clause.
public void readFile() throws IOException {
}
Exception handling is covered separately.

Native Methods

Native methods are implemented in languages such as C or C++ and invoked through the Java Native Interface (JNI).
public native void loadLibrary();
Native methods do not contain Java implementations.

synchronized Methods

The synchronized modifier ensures only one thread executes the method on a particular object at a time.
public synchronized void increment() {

    count++;
}
Synchronization is discussed in detail in the Multithreading and Concurrency article.

Method Invocation Stack

Each method invocation creates a new stack frame in the thread's call stack. The stack frame stores local variables, parameters, return information, and intermediate values.

When the method completes, its stack frame is automatically removed. Recursive method calls create multiple stack frames.

The JVM stack architecture is discussed in detail in the JVM Internals article.
Tail Recursion

Although some programming languages optimize tail-recursive methods, the Java compiler and JVM do not perform tail-call optimization. Large recursive calls may therefore consume significant stack memory. Iterative solutions are generally preferred for deep recursion.
Method Design Guidelines

Methods should perform one well-defined task, have descriptive names, minimize side effects, avoid excessive parameter lists, return meaningful values, and expose only the behavior required by callers. Small, cohesive methods improve readability, maintainability, testing, and reuse.

Final Notes

Methods define the executable behavior of Java classes. One of the most important language rules to remember is that Java always uses pass-by-value, regardless of whether primitive values or object references are passed to a method.
Nagesh Chauhan

Nagesh Chauhan

Principal Engineer | Java ยท Spring Boot ยท Python ยท Microservices ยท AI/ML

Principal Engineer with 14+ years of experience in designing scalable systems using Java, Spring Boot, and Python. Specialized in microservices architecture, system design, and machine learning.

Share this Article

๐Ÿ’ฌ Comments

Join the Discussion