Methods & Functions

Method declaration, parameters, and return types

1. Method Basics

Methods are reusable blocks of code that perform specific tasks. They help organize code, reduce repetition, and make programs easier to understand and maintain.

Method Structure

Access Modifier

static

Return Type

methodName

(parameters)

{ method body }

Basic Method Syntax

public class Calculator {
    // Method with return value
    public static int add(int a, int b) {
        return a + b;
    }

    // Method without return value
    public static void printResult(int result) {
        System.out.println("Result: " + result);
    }

    // Method without parameters
    public static void welcome() {
        System.out.println("Welcome to Calculator!");
    }
}

Access Modifiers

Control visibility: public, private, protected

Return Type

Data type returned: int, String, void

Method Name

Descriptive identifier following camelCase

Parameters

Input values passed to the method

2. Parameters & Arguments

Parameters are variables defined in a method signature, while arguments are the actual values passed when calling the method.

Parameter Flow

1

Method Call

add(5, 3)

2

Parameters

int a = 5, int b = 3

3

Processing

return a + b

4

Return

8

Parameter Types

public class ParameterExamples {
    // Primitive parameters (pass by value)
    public static void modifyPrimitive(int x) {
        x = 100; // Original value unchanged
    }

    // Object parameters (pass by reference)
    public static void modifyArray(int[] arr) {
        arr[0] = 999; // Original array modified
    }

    // Variable arguments (varargs)
    public static int sum(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }

    public static void main(String[] args) {
        // Primitive example
        int value = 10;
        modifyPrimitive(value);
        System.out.println(value); // Still 10

        // Array example
        int[] numbers = {1, 2, 3};
        modifyArray(numbers);
        System.out.println(numbers[0]); // Now 999

        // Varargs examples
        System.out.println(sum(1, 2, 3)); // 6
        System.out.println(sum(1, 2, 3, 4, 5)); // 15
    }
}

3. Method Overloading

Method overloading allows multiple methods with the same name but different parameter lists. The compiler determines which method to call based on the arguments.

Overloaded Method Signatures

  • calculate(int a, int b) - Addition
  • calculate(double a, double b) - Double Addition
  • calculate(int a, int b, int c) - Triple Addition
  • calculate(String operation, int a, int b) - Operation

Overloaded Methods

public class MathUtils {
    // Overloaded methods - same name, different parameters

    // Two integers
    public static int calculate(int a, int b) {
        return a + b;
    }

    // Two doubles
    public static double calculate(double a, double b) {
        return a + b;
    }

    // Three integers
    public static int calculate(int a, int b, int c) {
        return a + b + c;
    }

    // String operation with two integers
    public static int calculate(String operation, int a, int b) {
        switch(operation) {
            case "+": return a + b;
            case "-": return a - b;
            case "*": return a * b;
            case "/": return a / b;
            default: return 0;
        }
    }

    public static void main(String[] args) {
        System.out.println(calculate(5, 3));           // 8
        System.out.println(calculate(5.5, 3.2));       // 8.7
        System.out.println(calculate(1, 2, 3));        // 6
        System.out.println(calculate("*", 4, 5));      // 20
    }
}

4. Static vs Instance Methods

Understanding the difference between static and instance methods is crucial for proper object-oriented design.

Static Methods

  • Belong to the class
  • Called without object creation
  • Cannot access instance variables
  • Shared by all instances

Instance Methods

  • Belong to object instances
  • Require object creation
  • Can access instance variables
  • Unique to each instance

Static vs Instance Example

public class BankAccount {
    // Instance variables
    private String accountHolder;
    private double balance;

    // Static variable
    private static int totalAccounts = 0;

    // Constructor
    public BankAccount(String holder, double initialBalance) {
        this.accountHolder = holder;
        this.balance = initialBalance;
        totalAccounts++; // Increment static counter
    }

    // Instance method - operates on specific account
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited $" + amount);
        }
    }

    // Instance method - returns instance data
    public double getBalance() {
        return balance;
    }

    // Static method - operates on class-level data
    public static int getTotalAccounts() {
        return totalAccounts;
    }

    // Static utility method
    public static double calculateInterest(double principal, double rate) {
        return principal * rate / 100;
    }

    public static void main(String[] args) {
        // Static method call - no object needed
        System.out.println("Interest: $" +
            BankAccount.calculateInterest(1000, 5)); // $50.0

        // Create objects for instance methods
        BankAccount account1 = new BankAccount("Alice", 1000);
        BankAccount account2 = new BankAccount("Bob", 2000);

        // Instance method calls
        account1.deposit(500);
        System.out.println("Alice's balance: $" + account1.getBalance()); // $1500

        // Static method call
        System.out.println("Total accounts: " +
            BankAccount.getTotalAccounts()); // 2
    }
}

5. Variable Scope & Lifetime

Variable scope determines where a variable can be accessed. Understanding scope prevents bugs and helps write cleaner code.

Scope Hierarchy

  • Class Scope: static variables, instance variables
  • Method Scope: parameters, local variables
  • Block Scope: loop variables, if block variables

Scope Examples

public class ScopeExample {
    // Class-level variables
    static int classVariable = 100;     // Static - shared by all
    int instanceVariable = 200;         // Instance - per object

    public void demonstrateScope() {
        // Method-level variable
        int methodVariable = 300;

        System.out.println("Class variable: " + classVariable);       // Accessible
        System.out.println("Instance variable: " + instanceVariable); // Accessible
        System.out.println("Method variable: " + methodVariable);     // Accessible

        // Block scope
        if (true) {
            int blockVariable = 400;
            System.out.println("Block variable: " + blockVariable);   // Accessible
            System.out.println("Method variable: " + methodVariable); // Still accessible
        }

        // System.out.println(blockVariable); // Error - out of scope

        // Loop scope
        for (int i = 0; i 3; i++) {
            int loopVariable = i * 10;
            System.out.println("Loop variable: " + loopVariable);     // Accessible
        }

        // System.out.println(i); // Error - loop variable out of scope
    }

    public static void staticMethod() {
        System.out.println("Class variable: " + classVariable);       // Accessible
        // System.out.println(instanceVariable); // Error - no instance context

        // Need instance to access instance variables
        ScopeExample obj = new ScopeExample();
        System.out.println("Instance variable: " + obj.instanceVariable); // Now accessible
    }
}

6. Recursion Basics

Recursion is a technique where a method calls itself to solve a problem by breaking it into smaller subproblems.

Factorial Recursion: factorial(4)

4

factorial(4)

4 * factorial(3)

3

factorial(3)

3 * factorial(2)

2

factorial(2)

2 * factorial(1)

1

factorial(1)

return 1 (base case)

Result: 4 * 3 * 2 * 1 = 24

Recursion Examples

public class RecursionExamples {

    // Classic factorial
    public static long factorial(int n) {
        // Base case - stops recursion
        if (n 1) {
            return 1;
        }
        // Recursive case - method calls itself
        return n * factorial(n - 1);
    }

    // Fibonacci sequence
    public static int fibonacci(int n) {
        if (n 1) {
            return n;
        }
        return fibonacci(n - 1) + fibonacci(n - 2);
    }

    // Power calculation
    public static double power(double base, int exponent) {
        // Base cases
        if (exponent == 0) return 1;
        if (exponent == 1) return base;

        // Recursive cases
        if (exponent > 0) {
            return base * power(base, exponent - 1);
        } else {
            return 1 / power(base, -exponent);
        }
    }

    // Binary search (recursive)
    public static int binarySearch(int[] arr, int target, int left, int right) {
        if (left > right) {
            return -1; // Not found
        }

        int mid = left + (right - left) / 2;

        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] > target) {
            return binarySearch(arr, target, left, mid - 1);
        } else {
            return binarySearch(arr, target, mid + 1, right);
        }
    }

    public static void main(String[] args) {
        System.out.println("5! = " + factorial(5));           // 120
        System.out.println("Fib(6) = " + fibonacci(6));       // 8
        System.out.println("2^8 = " + power(2, 8));           // 256.0

        int[] sortedArray = {1, 3, 5, 7, 9, 11, 13};
        int index = binarySearch(sortedArray, 7, 0, sortedArray.length - 1);
        System.out.println("Found 7 at index: " + index);     // 3
    }
}

7. Best Practices

Following best practices when writing methods leads to cleaner, more maintainable code.

Clean Method Examples

public class CleanMethodExamples {

    // Good: Clear, specific name
    public static boolean isValidEmail(String email) {
        return email != null && email.contains("@") && email.contains(".");
    }

    // Good: Single responsibility
    public static double calculateDiscount(double price, int discountPercent) {
        return price * discountPercent / 100.0;
    }

    // Good: Input validation
    public static int divide(int dividend, int divisor) {
        if (divisor == 0) {
            throw new IllegalArgumentException("Division by zero");
        }
        return dividend / divisor;
    }

    // Good: Using objects for complex parameters
    public static class OrderRequest {
        private String productId;
        private int quantity;
        private String customerId;
        private boolean expressShipping;

        public OrderRequest(String productId, int quantity,
                           String customerId, boolean expressShipping) {
            this.productId = productId;
            this.quantity = quantity;
            this.customerId = customerId;
            this.expressShipping = expressShipping;
        }

        // Getters
        public String getProductId() { return productId; }
        public int getQuantity() { return quantity; }
        public String getCustomerId() { return customerId; }
        public boolean isExpressShipping() { return expressShipping; }
    }

    // Instead of: processOrder(String id, int qty, String customer, boolean express, ...)
    public static boolean processOrder(OrderRequest request) {
        System.out.println("Processing order for: " + request.getCustomerId());
        System.out.println("Product: " + request.getProductId());
        System.out.println("Quantity: " + request.getQuantity());
        return true;
    }

    // Good: Utility methods for common operations
    public static String formatCurrency(double amount) {
        return String.format("$%.2f", amount);
    }

    public static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }
}

Quick Reference

Access Modifiers:

  • public - Accessible everywhere
  • private - Only within same class
  • protected - Package + subclasses
  • (default) - Package-level access

Common Patterns:

  • static - Class-level method
  • final - Cannot be overridden
  • int... args - Variable arguments
  • @Override - Override parent method