Java Tutorial » Chapter 19 — Methods

Chapter 19 — Java Methods

Mastering method creation, overloading, and advanced techniques for modular programming.

1. Introduction

Understanding Methods in Java

In Java, a method is a collection of statements that perform a specific task. Methods are essential for organizing code into reusable blocks, making programs more modular and easier to maintain.

Methods are defined within classes and can be called to execute their code. They can accept inputs (parameters) and may return a value. Methods help eliminate code duplication and improve readability.

Java provides two types of methods:

  • Standard Library Methods: Built-in methods provided by Java (e.g., System.out.println(), Math.sqrt())
  • User-defined Methods: Methods created by programmers to perform specific tasks
2. Creating Methods

Method Syntax and Structure

A method in Java has the following general syntax:

returnType methodName(parameter1, parameter2, ...) {
    // Method body: code to be executed
    return value; // Only if returnType is not void
}

Let's break down each component:

  • returnType: The data type of the value the method returns. If the method doesn't return a value, use void.
  • methodName: The name of the method, which should follow Java naming conventions (camelCase).
  • parameters: Input values that the method accepts. Each parameter has a type and name.
  • method body: The code that executes when the method is called.
  • return statement: Returns a value of the specified returnType.
public class MethodExample {
    // Method that returns the sum of two integers
    public static int add(int a, int b) {
        int sum = a + b;
        return sum;
    }
    
    // Method that prints a greeting
    public static void greet(String name) {
        System.out.println("Hello, " + name + "!");
    }
    
    public static void main(String[] args) {
        // These methods will be called in the next section
    }
}
3. Method Calling

Invoking Methods

To use a method, you need to call or invoke it. For static methods (like those in our examples), you call them using the class name. For instance methods, you call them using an object reference.

public class MethodCalling {
    // Method that returns the sum of two integers
    public static int add(int a, int b) {
        return a + b;
    }
    
    // Method that prints a greeting
    public static void greet(String name) {
        System.out.println("Hello, " + name + "!");
    }
    
    public static void main(String[] args) {
        // Calling the greet method
        greet("Alice"); // Outputs: Hello, Alice!
        
        // Calling the add method and storing the result
        int result = add(5, 3);
        System.out.println("5 + 3 = " + result); // Outputs: 5 + 3 = 8
        
        // Using the method directly in an expression
        System.out.println("10 + 20 = " + add(10, 20)); // Outputs: 10 + 20 = 30
    }
}
4. The void Keyword

Methods That Don't Return Values

The void keyword is used to specify that a method doesn't return any value. These methods typically perform actions rather than calculations. For example, a method that prints a message or modifies an object's state might not need to return anything.

public class VoidExample {
        // A void method that prints a message
        public static void printMessage(String message) {
            System.out.println("Message: " + message);
            // No return statement is needed
        }
        
        // A method that returns a value
        public static int multiply(int a, int b) {
            return a * b;
        }
        
        public static void main(String[] args) {
            // Calling the void method
            printMessage("Hello, World!"); // Outputs: Message: Hello, World!
            
            // Calling the method that returns a value
            int product = multiply(4, 5);
            System.out.println("4 * 5 = " + product); // Outputs: 4 * 5 = 20
        }
    }
5. Passing Parameters by Value

Understanding Parameter Passing

In Java, parameters are passed by value. This means that when you pass a variable to a method, you're actually passing a copy of the variable's value. For primitive types, this means the method receives a copy of the actual value. For object types, the method receives a copy of the reference.

This has important implications:

  • For primitive types, changes to the parameter inside the method don't affect the original variable.
  • For object types, the method can modify the object's state, but cannot change the reference to point to a different object.
public class PassByValue {
        // Method that tries to modify a primitive parameter
        public static void modifyPrimitive(int num) {
            num = 100; // This only changes the local copy
            System.out.println("Inside method: " + num);
        }
        
        // Method that tries to modify an object parameter
        public static void modifyObject(StringBuilder sb) {
            sb.append(" World"); // This modifies the object's state
            sb = new StringBuilder("New object"); // This only changes the local reference
            System.out.println("Inside method: " + sb.toString());
        }
        
        public static void main(String[] args) {
            // Example with a primitive type
            int original = 10;
            System.out.println("Before method: " + original);
            modifyPrimitive(original);
            System.out.println("After method: " + original); // Still 10, not changed
            
            // Example with an object type
            StringBuilder sb = new StringBuilder("Hello");
            System.out.println("Before method: " + sb.toString());
            modifyObject(sb);
            System.out.println("After method: " + sb.toString()); // "Hello World", object was modified
        }
    }
6. Method Overloading

Multiple Methods with the Same Name

Method overloading allows you to define multiple methods with the same name but different parameter lists. The compiler determines which method to call based on the number and types of arguments passed.

Method overloading improves code readability by allowing you to use the same method name for similar operations that work with different types of inputs.

public class MethodOverloading {
    // Method that adds two integers
    public static int add(int a, int b) {
        System.out.println("Adding two integers");
        return a + b;
    }
    
    // Overloaded method that adds three integers
    public static int add(int a, int b, int c) {
        System.out.println("Adding three integers");
        return a + b + c;
    }
    
    // Overloaded method that adds two doubles
    public static double add(double a, double b) {
        System.out.println("Adding two doubles");
        return a + b;
    }
    
    public static void main(String[] args) {
        // Calls the first method
        int result1 = add(5, 3);
        System.out.println("Result: " + result1);
        
        // Calls the second method
        int result2 = add(5, 3, 2);
        System.out.println("Result: " + result2);
        
        // Calls the third method
        double result3 = add(5.5, 3.2);
        System.out.println("Result: " + result3);
    }
}
7. Command-Line Arguments

Passing Arguments to Your Program

Java allows you to pass arguments to your program from the command line. These arguments are captured in the String[] args parameter of the main method.

Command-line arguments are useful for providing input to your program without hardcoding values. They are always received as strings, so you may need to convert them to other types if needed.

public class CommandLineArgs {
    public static void main(String[] args) {
        // Check if any arguments were provided
        if (args.length == 0) {
            System.out.println("No command-line arguments provided.");
            System.out.println("Usage: java CommandLineArgs  ");
            return;
        }
        
        // The first argument is assumed to be a name
        String name = args[0];
        
        // The second argument is assumed to be an age (if provided)
        if (args.length > 1) {
            try {
                int age = Integer.parseInt(args[1]);
                System.out.println("Hello, " + name + "! You are " + age + " years old.");
            } catch (NumberFormatException e) {
                System.out.println("Invalid age format. Please provide a number.");
            }
        } else {
            System.out.println("Hello, " + name + "!");
        }
        
        // Print all arguments
        System.out.println("\nAll arguments provided:");
        for (int i = 0; i < args.length; i++) {
            System.out.println("Argument " + (i + 1) + ": " + args[i]);
        }
    }
}

To run this program with command-line arguments:

java CommandLineArgs Alice 25
8. The Constructors

Initializing Objects

A constructor is a special method that is called when an object is created. Its purpose is to initialize the object's state. Constructors have the same name as the class and no return type (not even void).

If you don't define any constructors, Java provides a default constructor that takes no arguments and initializes the fields to their default values.

public class Person {
    // Instance variables
    String name;
    int age;
    
    // Default constructor
    public Person() {
        name = "Unknown";
        age = 0;
        System.out.println("Default constructor called");
    }
    
    // Method to display person details
    public void displayInfo() {
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
    }
}

public class ConstructorExample {
    public static void main(String[] args) {
        // Creating an object using the default constructor
        Person person = new Person();
        person.displayInfo();
    }
}
9. Parameterized Constructor

Custom Object Initialization

A parameterized constructor accepts arguments to initialize the object's state with specific values. This allows you to create objects with different initial states.

You can have multiple constructors in a class, as long as they have different parameter lists (constructor overloading).

public class Person {
    // Instance variables
    String name;
    int age;
    
    // Default constructor
    public Person() {
        name = "Unknown";
        age = 0;
        System.out.println("Default constructor called");
    }
    
    // Parameterized constructor
    public Person(String name, int age) {
        this.name = name; // 'this' refers to the instance variable
        this.age = age;
        System.out.println("Parameterized constructor called");
    }
    
    // Method to display person details
    public void displayInfo() {
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
    }
}

public class ParameterizedConstructorExample {
    public static void main(String[] args) {
        // Creating an object using the default constructor
        Person person1 = new Person();
        person1.displayInfo();
        
        // Creating an object using the parameterized constructor
        Person person2 = new Person("Alice", 30);
        person2.displayInfo();
    }
}
10. The this Keyword

Referencing the Current Object

The this keyword in Java is a reference to the current object. It has several uses:

  • Distinguishing instance variables from parameters: When a parameter has the same name as an instance variable, this refers to the instance variable.
  • Calling one constructor from another: Using this() to call another constructor in the same class.
  • Passing the current object as a parameter: When a method needs a reference to the current object.
public class Person {
    // Instance variables
    String name;
    int age;
    
    // Default constructor
    public Person() {
        this("Unknown", 0); // Calls the parameterized constructor
    }
    
    // Parameterized constructor
    public Person(String name, int age) {
        this.name = name; // 'this.name' is the instance variable, 'name' is the parameter
        this.age = age;
    }
    
    // Method that returns the current object
    public Person getSelf() {
        return this; // Returns a reference to the current object
    }
    
    // Method that passes the current object to another method
    public void displayInfo() {
        printDetails(this); // Passes the current object to the printDetails method
    }
    
    // Helper method that prints the details of a person
    private void printDetails(Person person) {
        System.out.println("Name: " + person.name);
        System.out.println("Age: " + person.age);
    }
}

public class ThisKeywordExample {
    public static void main(String[] args) {
        // Creating an object using the default constructor
        Person person1 = new Person();
        person1.displayInfo();
        
        // Creating an object using the parameterized constructor
        Person person2 = new Person("Alice", 30);
        person2.displayInfo();
        
        // Getting a reference to the same object
        Person person3 = person2.getSelf();
        System.out.println("person2 == person3: " + (person2 == person3)); // true
    }
}
11. Variable Arguments (var-args)

Methods with a Variable Number of Arguments

Java 5 introduced variable arguments (var-args), which allow a method to accept a variable number of arguments of the same type. This is useful when you don't know in advance how many arguments will be passed.

Var-args are specified using an ellipsis (...) after the type. The var-args parameter must be the last parameter in the method signature.

public class VarArgsExample {
    // Method with a variable number of integer arguments
    public static int sum(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }
    
    // Method with a fixed parameter and variable arguments
    public static void printMessage(String prefix, String... messages) {
        System.out.print(prefix + ": ");
        for (String msg : messages) {
            System.out.print(msg + " ");
        }
        System.out.println();
    }
    
    public static void main(String[] args) {
        // Calling the sum method with different numbers of arguments
        System.out.println("Sum of 5 and 10: " + sum(5, 10));
        System.out.println("Sum of 1, 2, 3, 4, 5: " + sum(1, 2, 3, 4, 5));
        
        // Calling the printMessage method
        printMessage("Errors", "File not found", "Access denied");
        printMessage("Warnings", "Low memory", "Deprecated API");
    }
}
12. Practice & Challenge

Test Your Skills

  1. Write a class with a method that calculates the factorial of a number using recursion.
  2. Create a class with overloaded methods to find the maximum of two numbers, three numbers, and an array of numbers.
  3. Write a program that accepts command-line arguments and calculates their sum.
  4. Create a class with multiple constructors, including one that calls another using this().
  5. Write a method that uses var-args to find the average of any number of values.

🏆 Challenge: Calculator Class

Create a comprehensive Calculator class with the following features:

  • A parameterized constructor that initializes the calculator with an initial value.
  • Methods for basic operations (add, subtract, multiply, divide) that return the result.
  • Overloaded methods for each operation that can work with either a single value or an array of values.
  • A method that uses var-args to perform a series of operations.
  • A reset() method to set the calculator back to zero.
  • A getCurrentValue() method to get the current value.

public class Calculator {
    private double currentValue;
    
    // Parameterized constructor
    public Calculator(double initialValue) {
        this.currentValue = initialValue;
        System.out.println("Calculator initialized with value: " + initialValue);
    }
    
    // Method to get the current value
    public double getCurrentValue() {
        return currentValue;
    }
    
    // Method to reset the calculator
    public void reset() {
        currentValue = 0;
        System.out.println("Calculator reset to 0");
    }
    
    // Add method for a single value
    public double add(double value) {
        currentValue += value;
        System.out.println("Added " + value + ", current value: " + currentValue);
        return currentValue;
    }
    
    // Overloaded add method for multiple values
    public double add(double... values) {
        for (double value : values) {
            currentValue += value;
        }
        System.out.println("Added multiple values, current value: " + currentValue);
        return currentValue;
    }
    
    // Subtract method for a single value
    public double subtract(double value) {
        currentValue -= value;
        System.out.println("Subtracted " + value + ", current value: " + currentValue);
        return currentValue;
    }
    
    // Overloaded subtract method for multiple values
    public double subtract(double... values) {
        for (double value : values) {
            currentValue -= value;
        }
        System.out.println("Subtracted multiple values, current value: " + currentValue);
        return currentValue;
    }
    
    // Multiply method
    public double multiply(double value) {
        currentValue *= value;
        System.out.println("Multiplied by " + value + ", current value: " + currentValue);
        return currentValue;
    }
    
    // Divide method
    public double divide(double value) {
        if (value != 0) {
            currentValue /= value;
            System.out.println("Divided by " + value + ", current value: " + currentValue);
        } else {
            System.out.println("Cannot divide by zero!");
        }
        return currentValue;
    }
    
    // Method to perform a series of operations
    public void performOperations(String... operations) {
        for (String op : operations) {
            String[] parts = op.split(" ");
            String operation = parts[0];
            double value = Double.parseDouble(parts[1]);
            
            switch (operation) {
                case "add":
                    add(value);
                    break;
                case "subtract":
                    subtract(value);
                    break;
                case "multiply":
                    multiply(value);
                    break;
                case "divide":
                    divide(value);
                    break;
                default:
                    System.out.println("Unknown operation: " + operation);
            }
        }
    }
    
    public static void main(String[] args) {
        // Create a calculator with an initial value of 10
        Calculator calc = new Calculator(10);
        
        // Perform some operations
        calc.add(5);
        calc.subtract(3);
        calc.multiply(2);
        calc.divide(4);
        
        // Use overloaded methods
        calc.add(1, 2, 3);
        calc.subtract(1, 2);
        
        // Use the performOperations method
        calc.performOperations("add 10", "multiply 2", "subtract 5");
        
        // Get the final value
        System.out.println("Final value: " + calc.getCurrentValue());
    }
}