Java Programming Functions Guide
Modular Programming

Java Methods - Complete Guide

Master Java methods with detailed explanations of parameters, return types, overloading, recursion, and best practices for modular, reusable code.

Code Reusability

Write Once, Use Many Times

Modular Design

Break Complex Problems

Method Overloading

Multiple Versions

1. What are Methods in Java?

Methods in Java are blocks of code that perform specific tasks. They are the building blocks of Java programs, enabling code reuse, modularity, and organization.

Method Characteristics
  • Encapsulate functionality into reusable units
  • Can accept parameters (inputs)
  • Can return values (outputs)
  • Improve code readability and maintainability
  • Enable code reuse across program
  • Support recursion (calling themselves)
Types of Methods
  • Instance Methods: Operate on object instances
  • Static Methods: Belong to class, not instances
  • Abstract Methods: Declared without implementation
  • Final Methods: Cannot be overridden
  • Synchronized Methods: Thread-safe execution
  • Native Methods: Implemented in other languages

Quick Reference: Method Types

Different types of methods available in Java:

Instance Methods Static Methods Abstract Methods Final Methods Synchronized Native Methods Void Methods Return Type Methods

2. Method Anatomy and Syntax

Understanding the structure of a Java method is essential for writing effective code. Here's the complete anatomy:

Method Structure Breakdown
Access Modifier: Controls visibility (public, private, protected, package-private)
Return Type: Data type of value returned (void if no return)
Method Name: Identifier following camelCase convention
Parameters: Input values in parentheses (type name, type name, ...)
Method Body: Code block in curly braces { } containing implementation
public static int calculateSum(int a, int b) {
    int sum = a + b;
    return sum;
}
Component Description Required Examples
Access Modifier Controls visibility of method Optional (default: package-private) public, private, protected
Return Type Data type of returned value Required int, String, void, double
Method Name Identifier for method Required calculateSum, printMessage
Parameters Input values for method Optional (int x, int y)
Method Body Implementation code Required (except abstract) { return x + y; }
Exception List Exceptions method can throw Optional throws IOException
MethodAnatomyExamples.java
public class MethodAnatomyExamples {
    public static void main(String[] args) {
        System.out.println("=== METHOD ANATOMY EXAMPLES ===\n");
        
        // Call various method examples
        simpleMethod();
        
        String greeting = getGreeting("Alice");
        System.out.println(greeting);
        
        int result = addNumbers(5, 3);
        System.out.println("5 + 3 = " + result);
        
        double area = calculateCircleArea(7.0);
        System.out.println("Area of circle with radius 7: " + area);
        
        boolean isEven = checkEven(42);
        System.out.println("Is 42 even? " + isEven);
        
        String fullName = concatenateNames("John", "Doe");
        System.out.println("Full name: " + fullName);
    }
    
    // 1. Simple void method (no parameters, no return)
    public static void simpleMethod() {
        System.out.println("This is a simple method with no parameters and no return value.");
    }
    
    // 2. Method with parameters and return value
    public static String getGreeting(String name) {
        return "Hello, " + name + "! Welcome to Java methods.";
    }
    
    // 3. Method with multiple parameters
    public static int addNumbers(int a, int b) {
        return a + b;
    }
    
    // 4. Method with different return type
    public static double calculateCircleArea(double radius) {
        return Math.PI * radius * radius;
    }
    
    // 5. Method with boolean return type
    public static boolean checkEven(int number) {
        return number % 2 == 0;
    }
    
    // 6. Method that calls other methods
    public static String concatenateNames(String firstName, String lastName) {
        String fullName = formatName(firstName) + " " + formatName(lastName);
        return fullName.trim();
    }
    
    // 7. Helper method (private, used internally)
    private static String formatName(String name) {
        if (name == null || name.isEmpty()) {
            return "";
        }
        return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
    }
    
    // 8. Method with default (package-private) access
    static void packagePrivateMethod() {
        System.out.println("This method is accessible only within the package.");
    }
    
    // 9. Protected method
    protected static void protectedMethod() {
        System.out.println("This method is accessible within package and subclasses.");
    }
    
    // 10. Private method (internal use only)
    private static void privateMethod() {
        System.out.println("This method is accessible only within this class.");
    }
}

3. Method Types and Categories

Java methods can be categorized based on their behavior, access level, and relationship with objects.

Method Classification Chart

By Access Level
  • Public: Accessible from anywhere
  • Private: Accessible only within class
  • Protected: Accessible within package + subclasses
  • Package-private: Accessible only within package
By Return Type
  • Void Methods: No return value
  • Primitive Return: int, double, boolean, etc.
  • Object Return: String, Object, custom classes
  • Array Return: Returns arrays
Method Type Description When to Use Example
Instance Methods Operate on instance variables, require object When method needs object state person.getName()
Static Methods Belong to class, can be called without object Utility functions, helper methods Math.sqrt()
Abstract Methods Declared without implementation (in abstract class/interface) When implementation varies in subclasses abstract void draw();
Final Methods Cannot be overridden by subclasses When method implementation must not change public final void secureMethod()
Synchronized Thread-safe, only one thread can execute at a time Multi-threaded environments synchronized void update()
Native Methods Implemented in other languages (C/C++) Platform-specific operations public native void nativeOp();
Varargs Methods Accept variable number of arguments When number of parameters is unknown void printAll(String... args)
MethodTypesExamples.java
public class MethodTypesExamples {
    
    // Instance variable
    private int counter = 0;
    
    public static void main(String[] args) {
        System.out.println("=== METHOD TYPES DEMONSTRATION ===\n");
        
        // Static method call (no object needed)
        staticMethodExample();
        
        // Instance method call (requires object)
        MethodTypesExamples obj = new MethodTypesExamples();
        obj.instanceMethodExample();
        
        // Varargs method call
        printNumbers(1, 2, 3, 4, 5);
        printNumbers(); // No arguments
        
        // Method with array parameter
        int[] scores = {85, 92, 78, 90, 88};
        printArray(scores);
        
        // Method with object return
        Person person = createPerson("John", 30);
        System.out.println("Created: " + person);
        
        // Method with exception declaration
        try {
            riskyMethod();
        } catch (Exception e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }
    
    // ========== STATIC METHODS ==========
    
    // Static method - belongs to class
    public static void staticMethodExample() {
        System.out.println("This is a static method.");
        System.out.println("Can be called without creating an object.");
        System.out.println("Cannot access instance variables directly.");
    }
    
    // Static utility method
    public static double calculateAverage(int[] numbers) {
        if (numbers == null || numbers.length == 0) {
            return 0.0;
        }
        
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return (double) sum / numbers.length;
    }
    
    // ========== INSTANCE METHODS ==========
    
    // Instance method - operates on object state
    public void instanceMethodExample() {
        System.out.println("\nThis is an instance method.");
        System.out.println("Requires an object to be called.");
        System.out.println("Can access instance variables.");
        counter++;
        System.out.println("Counter incremented to: " + counter);
    }
    
    // Getter method (instance)
    public int getCounter() {
        return counter;
    }
    
    // Setter method (instance)
    public void setCounter(int value) {
        this.counter = value;
    }
    
    // ========== VARARGS METHODS ==========
    
    // Varargs method - variable number of arguments
    public static void printNumbers(int... numbers) {
        System.out.println("\nVarargs method called with " + numbers.length + " numbers:");
        for (int num : numbers) {
            System.out.print(num + " ");
        }
        System.out.println();
    }
    
    // ========== ARRAY PARAMETER METHODS ==========
    
    public static void printArray(int[] array) {
        System.out.println("\nArray contents:");
        for (int i = 0; i < array.length; i++) {
            System.out.println("array[" + i + "] = " + array[i]);
        }
    }
    
    // ========== OBJECT RETURN METHODS ==========
    
    public static Person createPerson(String name, int age) {
        return new Person(name, age);
    }
    
    // ========== EXCEPTION DECLARATION METHODS ==========
    
    public static void riskyMethod() throws Exception {
        System.out.println("\nThis method declares it might throw an exception.");
        // Simulating an exceptional condition
        if (Math.random() > 0.5) {
            throw new Exception("Something went wrong!");
        }
        System.out.println("Method completed successfully.");
    }
    
    // ========== FINAL METHOD ==========
    
    // Final method - cannot be overridden
    public final void finalMethodExample() {
        System.out.println("This is a final method. Cannot be overridden by subclasses.");
    }
    
    // ========== PRIVATE HELPER METHOD ==========
    
    private static void helperMethod() {
        System.out.println("This is a private helper method. Only accessible within this class.");
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // Getter methods
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    @Override
    public String toString() {
        return name + " (" + age + " years)";
    }
}
Choosing Method Types:
  • Use static methods for utility functions that don't need object state
  • Use instance methods when method needs to access or modify object data
  • Use private methods for internal implementation details
  • Use final methods when implementation should not be changed by subclasses
  • Use varargs when number of parameters is variable and unknown
  • Use synchronized in multi-threaded environments for thread safety

4. Parameters and Arguments

Parameters define what input a method expects, while arguments are the actual values passed when calling the method.

Method Call Stack Visualization
main() calling calculateSum(5, 3)
calculateSum(a=5, b=3) executing
Parameters receive values: a = 5, b = 3
Returning result: 8
Back to main(), result = 8
Parameter Type Description Syntax Example
Value Parameters Primitive types passed by value (copy) (int x, double y) modifyValue(x) doesn't change original
Reference Parameters Objects passed by reference (String str, Person p) modifyObject(obj) affects original
Varargs Variable number of arguments (String... names) printAll("A", "B", "C")
Array Parameters Arrays as parameters (int[] numbers) processArray(arr)
Default Parameters Not supported in Java (use overloading) N/A Use method overloading instead
Final Parameters Parameters that cannot be modified (final int x) Parameter value cannot be changed
ParametersExamples.java
public class ParametersExamples {
    public static void main(String[] args) {
        System.out.println("=== PARAMETERS AND ARGUMENTS ===\n");
        
        // Value parameters (primitives)
        System.out.println("1. Value Parameters (Primitives):");
        int originalValue = 10;
        System.out.println("Before method call: originalValue = " + originalValue);
        modifyPrimitive(originalValue);
        System.out.println("After method call: originalValue = " + originalValue);
        System.out.println("Note: Primitive parameters are passed by value (copy)\n");
        
        // Reference parameters (objects)
        System.out.println("2. Reference Parameters (Objects):");
        Person person = new Person("Alice", 25);
        System.out.println("Before method call: " + person);
        modifyObject(person);
        System.out.println("After method call: " + person);
        System.out.println("Note: Object parameters are passed by reference\n");
        
        // Array parameters
        System.out.println("3. Array Parameters:");
        int[] numbers = {1, 2, 3, 4, 5};
        System.out.println("Before: " + java.util.Arrays.toString(numbers));
        modifyArray(numbers);
        System.out.println("After: " + java.util.Arrays.toString(numbers));
        System.out.println("Note: Arrays are objects, so passed by reference\n");
        
        // Varargs parameters
        System.out.println("4. Varargs Parameters:");
        printAll("Apple", "Banana", "Cherry");
        printAll(); // Zero arguments
        printAll("Single"); // One argument
        System.out.println();
        
        // Multiple parameter types
        System.out.println("5. Multiple Parameter Types:");
        String result = formatMessage("Hello", 3, true, 42.5);
        System.out.println(result);
        System.out.println();
        
        // Final parameters
        System.out.println("6. Final Parameters:");
        processWithFinalParam(100);
        System.out.println();
        
        // Method overloading for default parameters simulation
        System.out.println("7. Simulating Default Parameters with Overloading:");
        greet("John");
        greet("Jane", "Good evening");
    }
    
    // ========== VALUE PARAMETERS (Primitives) ==========
    
    public static void modifyPrimitive(int value) {
        System.out.println("  Inside method - received value: " + value);
        value = 999; // Modifies local copy only
        System.out.println("  Inside method - changed to: " + value);
    }
    
    // ========== REFERENCE PARAMETERS (Objects) ==========
    
    public static void modifyObject(Person p) {
        System.out.println("  Inside method - received: " + p);
        p.setAge(30); // Modifies the original object
        System.out.println("  Inside method - modified age to 30");
    }
    
    // ========== ARRAY PARAMETERS ==========
    
    public static void modifyArray(int[] arr) {
        System.out.println("  Inside method - modifying array");
        for (int i = 0; i < arr.length; i++) {
            arr[i] = arr[i] * 2; // Modifies original array
        }
    }
    
    // ========== VARARGS PARAMETERS ==========
    
    public static void printAll(String... items) {
        System.out.print("  Received " + items.length + " items: ");
        for (String item : items) {
            System.out.print(item + " ");
        }
        System.out.println();
    }
    
    // Varargs must be last parameter
    public static void printWithPrefix(String prefix, String... items) {
        System.out.print(prefix + ": ");
        for (String item : items) {
            System.out.print(item + " ");
        }
        System.out.println();
    }
    
    // ========== MULTIPLE PARAMETER TYPES ==========
    
    public static String formatMessage(String text, int count, boolean flag, double value) {
        return String.format("Text: %s, Count: %d, Flag: %b, Value: %.2f", 
                            text, count, flag, value);
    }
    
    // ========== FINAL PARAMETERS ==========
    
    public static void processWithFinalParam(final int value) {
        System.out.println("  Received final parameter: " + value);
        // value = 200; // COMPILATION ERROR: Cannot assign value to final parameter
        int result = value * 2;
        System.out.println("  Result: " + result);
    }
    
    // ========== METHOD OVERLOADING FOR DEFAULT PARAMETERS ==========
    
    // Simulating default parameters (not available in Java)
    public static void greet(String name) {
        greet(name, "Hello"); // Call overloaded version with default
    }
    
    public static void greet(String name, String greeting) {
        System.out.println(greeting + ", " + name + "!");
    }
    
    // ========== PARAMETER VALIDATION ==========
    
    public static double calculateBMI(double weight, double height) {
        // Validate parameters
        if (weight <= 0) {
            throw new IllegalArgumentException("Weight must be positive");
        }
        if (height <= 0) {
            throw new IllegalArgumentException("Height must be positive");
        }
        
        return weight / (height * height);
    }
    
    // ========== RETURNING MULTIPLE VALUES ==========
    
    // Java doesn't support multiple return values directly
    // Options: Return array, return object, or use parameter mutation
    
    // Option 1: Return array
    public static int[] getMinMax(int[] numbers) {
        if (numbers == null || numbers.length == 0) {
            return new int[]{0, 0};
        }
        
        int min = numbers[0];
        int max = numbers[0];
        
        for (int num : numbers) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        return new int[]{min, max};
    }
    
    // Option 2: Return custom object
    public static MinMaxResult findMinMax(int[] numbers) {
        if (numbers == null || numbers.length == 0) {
            return new MinMaxResult(0, 0);
        }
        
        int min = numbers[0];
        int max = numbers[0];
        
        for (int num : numbers) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        return new MinMaxResult(min, max);
    }
    
    // Option 3: Modify parameters (pass by reference)
    public static void findMinMax(int[] numbers, int[] result) {
        if (numbers == null || numbers.length == 0) {
            result[0] = 0;
            result[1] = 0;
            return;
        }
        
        int min = numbers[0];
        int max = numbers[0];
        
        for (int num : numbers) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        result[0] = min;
        result[1] = max;
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() { return name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    
    @Override
    public String toString() {
        return name + " (" + age + " years)";
    }
}

// Helper class for returning multiple values
class MinMaxResult {
    private int min;
    private int max;
    
    public MinMaxResult(int min, int max) {
        this.min = min;
        this.max = max;
    }
    
    public int getMin() { return min; }
    public int getMax() { return max; }
    
    @Override
    public String toString() {
        return "Min: " + min + ", Max: " + max;
    }
}

5. Return Types and Values

The return type specifies what type of value a method returns. Methods can return primitive types, objects, arrays, or nothing (void).

Return Type Description Example Method Usage
void No return value (performs action only) public void printMessage() When method only performs side effects
Primitive Types int, double, boolean, char, etc. public int calculateSum() When method computes a simple value
Object Types String, custom classes, Object public String getName() When method returns complex data
Arrays Array of any type public int[] getNumbers() When method returns collection of values
null Can be returned for object types return null; When no valid object to return
this Returns current object instance return this; For method chaining (builder pattern)
ReturnTypesExamples.java
public class ReturnTypesExamples {
    public static void main(String[] args) {
        System.out.println("=== RETURN TYPES AND VALUES ===\n");
        
        // 1. Void methods (no return)
        System.out.println("1. Void Methods:");
        printWelcomeMessage();
        System.out.println();
        
        // 2. Primitive return types
        System.out.println("2. Primitive Return Types:");
        int sum = add(10, 20);
        System.out.println("Sum: " + sum);
        
        double average = calculateAverage(85, 92, 78);
        System.out.println("Average: " + average);
        
        boolean isAdult = checkAge(25);
        System.out.println("Is adult? " + isAdult);
        System.out.println();
        
        // 3. Object return types
        System.out.println("3. Object Return Types:");
        String fullName = getFullName("John", "Doe");
        System.out.println("Full Name: " + fullName);
        
        Person person = createPerson("Alice", 30);
        System.out.println("Created: " + person);
        System.out.println();
        
        // 4. Array return types
        System.out.println("4. Array Return Types:");
        int[] squares = generateSquares(5);
        System.out.println("Squares: " + java.util.Arrays.toString(squares));
        
        String[] colors = getColors();
        System.out.println("Colors: " + java.util.Arrays.toString(colors));
        System.out.println();
        
        // 5. Returning null
        System.out.println("5. Returning null:");
        String result = findByName("Unknown");
        System.out.println("Search result: " + (result == null ? "Not found" : result));
        System.out.println();
        
        // 6. Method chaining with 'this' return
        System.out.println("6. Method Chaining:");
        Calculator calc = new Calculator(10);
        calc.add(5).multiply(2).subtract(3).printResult();
        System.out.println();
        
        // 7. Early return
        System.out.println("7. Early Return:");
        int number = -5;
        String validation = validateNumber(number);
        System.out.println("Validation for " + number + ": " + validation);
        
        number = 42;
        validation = validateNumber(number);
        System.out.println("Validation for " + number + ": " + validation);
        System.out.println();
        
        // 8. Returning from void methods
        System.out.println("8. Returning from void methods:");
        processNumber(0);
        processNumber(10);
        System.out.println();
        
        // 9. Return in try-catch-finally
        System.out.println("9. Return in try-catch-finally:");
        int value = riskyOperation();
        System.out.println("Result: " + value);
    }
    
    // ========== VOID METHODS ==========
    
    public static void printWelcomeMessage() {
        System.out.println("Welcome to Java Methods Tutorial!");
        System.out.println("This method performs an action but returns nothing.");
        // No return statement needed (or can use empty return)
        // return; // Optional
    }
    
    // ========== PRIMITIVE RETURN TYPES ==========
    
    public static int add(int a, int b) {
        return a + b;
    }
    
    public static double calculateAverage(int a, int b, int c) {
        return (a + b + c) / 3.0;
    }
    
    public static boolean checkAge(int age) {
        return age >= 18;
    }
    
    // ========== OBJECT RETURN TYPES ==========
    
    public static String getFullName(String firstName, String lastName) {
        return firstName + " " + lastName;
    }
    
    public static Person createPerson(String name, int age) {
        return new Person(name, age);
    }
    
    // ========== ARRAY RETURN TYPES ==========
    
    public static int[] generateSquares(int n) {
        int[] squares = new int[n];
        for (int i = 0; i < n; i++) {
            squares[i] = (i + 1) * (i + 1);
        }
        return squares;
    }
    
    public static String[] getColors() {
        return new String[]{"Red", "Green", "Blue", "Yellow"};
    }
    
    // ========== RETURNING NULL ==========
    
    public static String findByName(String name) {
        String[] database = {"John", "Alice", "Bob"};
        
        for (String entry : database) {
            if (entry.equals(name)) {
                return entry;
            }
        }
        
        return null; // Not found
    }
    
    // ========== EARLY RETURN ==========
    
    public static String validateNumber(int number) {
        // Early return for invalid cases
        if (number < 0) {
            return "Error: Number cannot be negative";
        }
        
        if (number > 100) {
            return "Error: Number cannot exceed 100";
        }
        
        // Main logic for valid cases
        if (number % 2 == 0) {
            return "Valid: Even number";
        } else {
            return "Valid: Odd number";
        }
    }
    
    // ========== RETURNING FROM VOID METHODS ==========
    
    public static void processNumber(int num) {
        if (num == 0) {
            System.out.println("Number is zero, exiting early");
            return; // Exit method early
        }
        
        System.out.println("Processing number: " + num);
        // More processing...
    }
    
    // ========== RETURN IN TRY-CATCH-FINALLY ==========
    
    public static int riskyOperation() {
        try {
            System.out.println("Trying risky operation...");
            // Simulate risky code
            if (Math.random() > 0.5) {
                throw new RuntimeException("Something went wrong!");
            }
            return 42; // Return from try block
        } catch (RuntimeException e) {
            System.out.println("Caught exception: " + e.getMessage());
            return -1; // Return from catch block
        } finally {
            System.out.println("Finally block always executes");
            // Note: If finally has return, it overrides try/catch returns
            // return 0; // This would override previous returns
        }
    }
    
    // ========== RETURNING MULTIPLE VALUES ==========
    
    // Using array
    public static int[] getStatistics(int[] numbers) {
        if (numbers == null || numbers.length == 0) {
            return new int[]{0, 0, 0};
        }
        
        int sum = 0;
        int min = numbers[0];
        int max = numbers[0];
        
        for (int num : numbers) {
            sum += num;
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        return new int[]{sum, min, max};
    }
    
    // Using custom class (better approach)
    public static Statistics calculateStats(int[] numbers) {
        if (numbers == null || numbers.length == 0) {
            return new Statistics(0, 0, 0, 0);
        }
        
        int sum = 0;
        int min = numbers[0];
        int max = numbers[0];
        
        for (int num : numbers) {
            sum += num;
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        double average = (double) sum / numbers.length;
        return new Statistics(sum, min, max, average);
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return name + " (" + age + " years)";
    }
}

// For method chaining example
class Calculator {
    private int result;
    
    public Calculator(int initial) {
        this.result = initial;
    }
    
    public Calculator add(int value) {
        result += value;
        return this; // Return this for chaining
    }
    
    public Calculator subtract(int value) {
        result -= value;
        return this;
    }
    
    public Calculator multiply(int value) {
        result *= value;
        return this;
    }
    
    public void printResult() {
        System.out.println("Result: " + result);
    }
}

// For returning multiple values
class Statistics {
    private int sum;
    private int min;
    private int max;
    private double average;
    
    public Statistics(int sum, int min, int max, double average) {
        this.sum = sum;
        this.min = min;
        this.max = max;
        this.average = average;
    }
    
    @Override
    public String toString() {
        return String.format("Sum: %d, Min: %d, Max: %d, Avg: %.2f", 
                           sum, min, max, average);
    }
}

6. Method Overloading

Method overloading allows multiple methods with the same name but different parameters. It enables polymorphism at compile-time.

Overloading Rules

What CAN Change
  • Number of parameters
  • Type of parameters
  • Order of parameters
What CANNOT Change
  • Method name only
  • Return type only
  • Access modifier only
  • Exception list only
MethodOverloadingExamples.java
public class MethodOverloadingExamples {
    public static void main(String[] args) {
        System.out.println("=== METHOD OVERLOADING ===\n");
        
        // Example 1: Different number of parameters
        System.out.println("1. Different Number of Parameters:");
        System.out.println("add(10, 20) = " + add(10, 20));
        System.out.println("add(10, 20, 30) = " + add(10, 20, 30));
        System.out.println("add(10, 20, 30, 40) = " + add(10, 20, 30, 40));
        System.out.println();
        
        // Example 2: Different types of parameters
        System.out.println("2. Different Types of Parameters:");
        System.out.println("multiply(5, 3) = " + multiply(5, 3));
        System.out.println("multiply(2.5, 4.2) = " + multiply(2.5, 4.2));
        System.out.println("multiply(\"Hello\", 3) = " + multiply("Hello", 3));
        System.out.println();
        
        // Example 3: Different order of parameters
        System.out.println("3. Different Order of Parameters:");
        printInfo("John", 30);
        printInfo(25, "Alice");
        System.out.println();
        
        // Example 4: Overloading with type promotion
        System.out.println("4. Type Promotion in Overloading:");
        testPromotion(10);    // int -> long
        testPromotion(10.0f); // float -> double
        testPromotion('A');   // char -> int
        System.out.println();
        
        // Example 5: Overloading with varargs
        System.out.println("5. Overloading with Varargs:");
        display("Hello");
        display("Hello", "World");
        display("Hello", "World", "Java");
        System.out.println();
        
        // Example 6: Ambiguity in overloading
        System.out.println("6. Ambiguity Examples (commented out):");
        // ambiguousCall(10, 20); // COMPILATION ERROR: Ambiguous method call
        System.out.println();
        
        // Example 7: Real-world example - Calculator
        System.out.println("7. Real-world Example - Calculator:");
        Calculator calc = new Calculator();
        System.out.println("Int addition: " + calc.add(5, 3));
        System.out.println("Double addition: " + calc.add(5.5, 3.2));
        System.out.println("Three numbers: " + calc.add(1, 2, 3));
        System.out.println("Array sum: " + calc.add(new int[]{1, 2, 3, 4, 5}));
        System.out.println();
        
        // Example 8: Constructor overloading
        System.out.println("8. Constructor Overloading:");
        Student student1 = new Student();
        Student student2 = new Student("John Doe");
        Student student3 = new Student("Alice Smith", "CS101");
        Student student4 = new Student("Bob Johnson", "MA202", 3.8);
        
        System.out.println(student1);
        System.out.println(student2);
        System.out.println(student3);
        System.out.println(student4);
    }
    
    // ========== OVERLOADING BY PARAMETER COUNT ==========
    
    // Two parameters
    public static int add(int a, int b) {
        System.out.println("  Called: add(int, int)");
        return a + b;
    }
    
    // Three parameters
    public static int add(int a, int b, int c) {
        System.out.println("  Called: add(int, int, int)");
        return a + b + c;
    }
    
    // Four parameters
    public static int add(int a, int b, int c, int d) {
        System.out.println("  Called: add(int, int, int, int)");
        return a + b + c + d;
    }
    
    // Varargs version (handles any number)
    public static int add(int... numbers) {
        System.out.println("  Called: add(int...) with " + numbers.length + " numbers");
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
    
    // ========== OVERLOADING BY PARAMETER TYPE ==========
    
    // Integer multiplication
    public static int multiply(int a, int b) {
        System.out.println("  Called: multiply(int, int)");
        return a * b;
    }
    
    // Double multiplication
    public static double multiply(double a, double b) {
        System.out.println("  Called: multiply(double, double)");
        return a * b;
    }
    
    // String repetition
    public static String multiply(String str, int times) {
        System.out.println("  Called: multiply(String, int)");
        return str.repeat(times);
    }
    
    // ========== OVERLOADING BY PARAMETER ORDER ==========
    
    public static void printInfo(String name, int age) {
        System.out.println("  Called: printInfo(String, int)");
        System.out.println("  Name: " + name + ", Age: " + age);
    }
    
    public static void printInfo(int age, String name) {
        System.out.println("  Called: printInfo(int, String)");
        System.out.println("  Age: " + age + ", Name: " + name);
    }
    
    // ========== TYPE PROMOTION IN OVERLOADING ==========
    
    public static void testPromotion(long num) {
        System.out.println("  Called: testPromotion(long) with value: " + num);
    }
    
    public static void testPromotion(double num) {
        System.out.println("  Called: testPromotion(double) with value: " + num);
    }
    
    // Note: No testPromotion(int) method exists
    // int will be promoted to long
    // float will be promoted to double
    
    // ========== VARARGS OVERLOADING ==========
    
    public static void display(String message) {
        System.out.println("  Called: display(String)");
        System.out.println("  Message: " + message);
    }
    
    public static void display(String message1, String message2) {
        System.out.println("  Called: display(String, String)");
        System.out.println("  Messages: " + message1 + ", " + message2);
    }
    
    public static void display(String... messages) {
        System.out.println("  Called: display(String...) with " + messages.length + " messages");
        for (String msg : messages) {
            System.out.println("  - " + msg);
        }
    }
    
    // ========== AMBIGUITY IN OVERLOADING ==========
    
    public static void ambiguousCall(int a, double b) {
        System.out.println("int, double version");
    }
    
    public static void ambiguousCall(double a, int b) {
        System.out.println("double, int version");
    }
    
    // Calling ambiguousCall(10, 20) causes compilation error
    // Both methods are equally applicable
}

// ========== REAL-WORLD CALCULATOR EXAMPLE ==========

class Calculator {
    
    // Overloaded add methods
    
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    public int add(int[] numbers) {
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
    
    // Overloaded max methods
    
    public int max(int a, int b) {
        return (a > b) ? a : b;
    }
    
    public int max(int a, int b, int c) {
        return max(max(a, b), c);
    }
    
    public double max(double a, double b) {
        return (a > b) ? a : b;
    }
    
    // Overloaded print methods
    
    public void print(int value) {
        System.out.println("Integer: " + value);
    }
    
    public void print(double value) {
        System.out.println("Double: " + value);
    }
    
    public void print(String value) {
        System.out.println("String: " + value);
    }
    
    public void print(int[] array) {
        System.out.print("Array: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
        System.out.println();
    }
}

// ========== CONSTRUCTOR OVERLOADING EXAMPLE ==========

class Student {
    private String name;
    private String course;
    private double gpa;
    
    // Default constructor
    public Student() {
        this.name = "Unknown";
        this.course = "Not enrolled";
        this.gpa = 0.0;
    }
    
    // Constructor with name only
    public Student(String name) {
        this.name = name;
        this.course = "Not enrolled";
        this.gpa = 0.0;
    }
    
    // Constructor with name and course
    public Student(String name, String course) {
        this.name = name;
        this.course = course;
        this.gpa = 0.0;
    }
    
    // Full constructor
    public Student(String name, String course, double gpa) {
        this.name = name;
        this.course = course;
        this.gpa = gpa;
    }
    
    // Copy constructor
    public Student(Student other) {
        this.name = other.name;
        this.course = other.course;
        this.gpa = other.gpa;
    }
    
    @Override
    public String toString() {
        return String.format("%s - %s (GPA: %.2f)", name, course, gpa);
    }
}
Important Overloading Rules:
  • Overloaded methods MUST have different parameter lists
  • Return type alone is NOT sufficient for overloading
  • Access modifiers alone are NOT sufficient for overloading
  • Exception lists alone are NOT sufficient for overloading
  • Java compiler uses method signature (name + parameters) to resolve calls
  • Type promotion happens when exact match not found (int → long → float → double)
  • Avoid ambiguous overloading that causes compilation errors

7. Recursion

Recursion occurs when a method calls itself. It's a powerful technique for solving problems that can be broken down into smaller, similar subproblems.

Recursive Call Stack - factorial(4)
factorial(4) calls factorial(3)
4 * factorial(3) waiting...
factorial(3) calls factorial(2)
3 * factorial(2) waiting...
factorial(2) calls factorial(1)
2 * factorial(1) waiting...
factorial(1) returns 1 (base case)
factorial(2) returns 2 * 1 = 2
factorial(3) returns 3 * 2 = 6
factorial(4) returns 4 * 6 = 24
Recursion Type Description When to Use Example
Direct Recursion Method calls itself directly Simple recursive problems factorial(n)
Indirect Recursion Method A calls B, B calls A Mutually recursive algorithms isEven() ↔ isOdd()
Tail Recursion Recursive call is last operation Can be optimized by compiler factorialTail(n, acc)
Tree Recursion Multiple recursive calls Tree traversal, Fibonacci fibonacci(n)
Nested Recursion Recursive call has recursive parameter Complex mathematical functions Ackermann(m, n)
RecursionExamples.java
public class RecursionExamples {
    public static void main(String[] args) {
        System.out.println("=== RECURSION EXAMPLES ===\n");
        
        // 1. Factorial (classic recursion)
        System.out.println("1. Factorial:");
        for (int i = 0; i <= 5; i++) {
            System.out.println(i + "! = " + factorial(i));
        }
        System.out.println();
        
        // 2. Fibonacci sequence
        System.out.println("2. Fibonacci Sequence:");
        for (int i = 0; i <= 6; i++) {
            System.out.println("fib(" + i + ") = " + fibonacci(i));
        }
        System.out.println();
        
        // 3. Tail recursion factorial
        System.out.println("3. Tail Recursion Factorial:");
        System.out.println("5! = " + factorialTail(5, 1));
        System.out.println();
        
        // 4. Indirect recursion
        System.out.println("4. Indirect Recursion:");
        System.out.println("isEven(4): " + isEven(4));
        System.out.println("isOdd(4): " + isOdd(4));
        System.out.println("isEven(5): " + isEven(5));
        System.out.println("isOdd(5): " + isOdd(5));
        System.out.println();
        
        // 5. Power calculation
        System.out.println("5. Power Calculation:");
        System.out.println("2^5 = " + power(2, 5));
        System.out.println("3^4 = " + power(3, 4));
        System.out.println("5^0 = " + power(5, 0));
        System.out.println();
        
        // 6. Sum of digits
        System.out.println("6. Sum of Digits:");
        System.out.println("sumDigits(12345) = " + sumDigits(12345));
        System.out.println("sumDigits(987) = " + sumDigits(987));
        System.out.println();
        
        // 7. GCD calculation
        System.out.println("7. GCD (Euclidean Algorithm):");
        System.out.println("gcd(48, 18) = " + gcd(48, 18));
        System.out.println("gcd(56, 98) = " + gcd(56, 98));
        System.out.println();
        
        // 8. Palindrome check
        System.out.println("8. Palindrome Check:");
        System.out.println("isPalindrome(\"racecar\"): " + isPalindrome("racecar"));
        System.out.println("isPalindrome(\"hello\"): " + isPalindrome("hello"));
        System.out.println("isPalindrome(\"madam\"): " + isPalindrome("madam"));
        System.out.println();
        
        // 9. Binary search (recursive)
        System.out.println("9. Recursive Binary Search:");
        int[] sortedArray = {2, 5, 8, 12, 16, 23, 38, 45, 56, 72};
        int key = 23;
        int index = binarySearch(sortedArray, key, 0, sortedArray.length - 1);
        System.out.println("Search for " + key + ": index = " + index);
        System.out.println();
        
        // 10. Tower of Hanoi
        System.out.println("10. Tower of Hanoi:");
        towerOfHanoi(3, 'A', 'C', 'B');
        System.out.println();
        
        // 11. Tree traversal simulation
        System.out.println("11. Binary Tree Traversal:");
        TreeNode root = createSampleTree();
        System.out.print("Pre-order: ");
        preOrder(root);
        System.out.println();
        
        System.out.print("In-order: ");
        inOrder(root);
        System.out.println();
        
        System.out.print("Post-order: ");
        postOrder(root);
        System.out.println();
    }
    
    // ========== FACTORIAL (Classic Recursion) ==========
    
    public static int factorial(int n) {
        // Base case
        if (n <= 1) {
            return 1;
        }
        // Recursive case
        return n * factorial(n - 1);
    }
    
    // ========== FIBONACCI ==========
    
    public static int fibonacci(int n) {
        // Base cases
        if (n == 0) return 0;
        if (n == 1) return 1;
        
        // Recursive case (tree recursion)
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
    
    // ========== TAIL RECURSION FACTORIAL ==========
    
    public static int factorialTail(int n, int accumulator) {
        // Base case
        if (n <= 1) {
            return accumulator;
        }
        // Tail recursive call
        return factorialTail(n - 1, n * accumulator);
    }
    
    // ========== INDIRECT RECURSION ==========
    
    public static boolean isEven(int n) {
        if (n == 0) return true;
        return isOdd(n - 1);
    }
    
    public static boolean isOdd(int n) {
        if (n == 0) return false;
        return isEven(n - 1);
    }
    
    // ========== POWER CALCULATION ==========
    
    public static int power(int base, int exponent) {
        // Base case
        if (exponent == 0) return 1;
        
        // Recursive case
        return base * power(base, exponent - 1);
    }
    
    // Optimized power (logarithmic time)
    public static int powerFast(int base, int exponent) {
        if (exponent == 0) return 1;
        
        int halfPower = powerFast(base, exponent / 2);
        
        if (exponent % 2 == 0) {
            return halfPower * halfPower;
        } else {
            return base * halfPower * halfPower;
        }
    }
    
    // ========== SUM OF DIGITS ==========
    
    public static int sumDigits(int number) {
        // Base case
        if (number == 0) return 0;
        
        // Recursive case
        return (number % 10) + sumDigits(number / 10);
    }
    
    // ========== GCD (EUCLIDEAN ALGORITHM) ==========
    
    public static int gcd(int a, int b) {
        // Base case
        if (b == 0) return a;
        
        // Recursive case
        return gcd(b, a % b);
    }
    
    // ========== PALINDROME CHECK ==========
    
    public static boolean isPalindrome(String str) {
        return isPalindromeHelper(str, 0, str.length() - 1);
    }
    
    private static boolean isPalindromeHelper(String str, int left, int right) {
        // Base cases
        if (left >= right) return true;
        if (str.charAt(left) != str.charAt(right)) return false;
        
        // Recursive case
        return isPalindromeHelper(str, left + 1, right - 1);
    }
    
    // ========== BINARY SEARCH ==========
    
    public static int binarySearch(int[] array, int key, int low, int high) {
        // Base case: element not found
        if (low > high) return -1;
        
        int mid = (low + high) / 2;
        
        // Base case: element found
        if (array[mid] == key) return mid;
        
        // Recursive cases
        if (array[mid] > key) {
            return binarySearch(array, key, low, mid - 1);
        } else {
            return binarySearch(array, key, mid + 1, high);
        }
    }
    
    // ========== TOWER OF HANOI ==========
    
    public static void towerOfHanoi(int disks, char source, char destination, char auxiliary) {
        // Base case
        if (disks == 1) {
            System.out.println("Move disk 1 from " + source + " to " + destination);
            return;
        }
        
        // Move n-1 disks from source to auxiliary
        towerOfHanoi(disks - 1, source, auxiliary, destination);
        
        // Move the nth disk from source to destination
        System.out.println("Move disk " + disks + " from " + source + " to " + destination);
        
        // Move n-1 disks from auxiliary to destination
        towerOfHanoi(disks - 1, auxiliary, destination, source);
    }
    
    // ========== BINARY TREE TRAVERSAL ==========
    
    static class TreeNode {
        int value;
        TreeNode left;
        TreeNode right;
        
        TreeNode(int value) {
            this.value = value;
        }
    }
    
    public static TreeNode createSampleTree() {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);
        return root;
    }
    
    public static void preOrder(TreeNode node) {
        if (node == null) return;
        
        System.out.print(node.value + " ");
        preOrder(node.left);
        preOrder(node.right);
    }
    
    public static void inOrder(TreeNode node) {
        if (node == null) return;
        
        inOrder(node.left);
        System.out.print(node.value + " ");
        inOrder(node.right);
    }
    
    public static void postOrder(TreeNode node) {
        if (node == null) return;
        
        postOrder(node.left);
        postOrder(node.right);
        System.out.print(node.value + " ");
    }
    
    // ========== RECURSION VS ITERATION ==========
    
    // Iterative factorial
    public static int factorialIterative(int n) {
        int result = 1;
        for (int i = 1; i <= n; i++) {
            result *= i;
        }
        return result;
    }
    
    // Iterative Fibonacci
    public static int fibonacciIterative(int n) {
        if (n <= 1) return n;
        
        int prev = 0;
        int current = 1;
        
        for (int i = 2; i <= n; i++) {
            int next = prev + current;
            prev = current;
            current = next;
        }
        
        return current;
    }
    
    // ========== COMMON RECURSION MISTAKES ==========
    
    // Missing base case (infinite recursion)
    public static void infiniteRecursion() {
        infiniteRecursion(); // StackOverflowError!
    }
    
    // Wrong base case
    public static int wrongFactorial(int n) {
        if (n == 0) {
            return 0; // WRONG! Should return 1
        }
        return n * wrongFactorial(n - 1);
    }
    
    // Not progressing toward base case
    public static void noProgress(int n) {
        if (n <= 0) return;
        noProgress(n); // Never changes n - infinite recursion!
    }
}
Recursion Best Practices:
  • Always define a base case to prevent infinite recursion
  • Ensure each recursive call progresses toward the base case
  • Consider stack overflow for deep recursion (use iteration or tail recursion)
  • Use recursion for problems with natural recursive structure (trees, divide-and-conquer)
  • Consider memoization for overlapping subproblems (like Fibonacci)
  • Use tail recursion when possible for potential compiler optimization
  • Always test with edge cases (0, 1, negative numbers)

8. Method Design Principles and Best Practices

Common Method Design Mistakes:
  1. Methods too long: Should do one thing well (Single Responsibility)
  2. Too many parameters: Consider using objects or builder pattern
  3. Side effects: Methods should have predictable behavior
  4. Poor naming: Method names should indicate purpose
  5. No parameter validation: Always validate input parameters
  6. Deep nesting: Makes code hard to read and maintain
  7. Ignoring exceptions: Handle or declare checked exceptions
Method Design Principles
  • Single Responsibility: One method, one purpose
  • Command-Query Separation: Methods should either do something or answer something, not both
  • Fail Fast: Validate parameters early
  • Defensive Programming: Handle nulls and edge cases
  • Law of Demeter: Don't talk to strangers (minimize dependencies)
  • Keep It Simple: Simple, clear, maintainable code
  • Documentation: Javadoc for public methods
Performance Tips
  • Avoid unnecessary object creation in loops
  • Use primitive types when possible
  • Consider method inlining for small, frequently called methods
  • Use final for methods that shouldn't be overridden
  • Cache results of expensive computations
  • Use StringBuilder for string concatenation in loops
  • Profile before optimizing
MethodBestPractices.java
import java.util.Objects;

/**
 * Demonstration of method design best practices.
 * This class shows how to write clean, maintainable methods.
 */
public class MethodBestPractices {
    
    /**
     * Calculates the area of a rectangle.
     * 
     * @param width the width of the rectangle (must be positive)
     * @param height the height of the rectangle (must be positive)
     * @return the area of the rectangle
     * @throws IllegalArgumentException if width or height is not positive
     */
    public static double calculateRectangleArea(double width, double height) {
        // 1. Validate parameters early (Fail Fast)
        validateDimensions(width, height);
        
        // 2. Single responsibility - just calculates area
        return width * height;
    }
    
    /**
     * Validates rectangle dimensions.
     * Private helper method for parameter validation.
     */
    private static void validateDimensions(double width, double height) {
        if (width <= 0) {
            throw new IllegalArgumentException("Width must be positive: " + width);
        }
        if (height <= 0) {
            throw new IllegalArgumentException("Height must be positive: " + height);
        }
    }
    
    /**
     * Formats a user's full name.
     * Example of Command-Query Separation: returns formatted name without side effects.
     */
    public static String formatFullName(String firstName, String lastName) {
        // Defensive programming: handle nulls
        firstName = Objects.toString(firstName, "").trim();
        lastName = Objects.toString(lastName, "").trim();
        
        if (firstName.isEmpty() && lastName.isEmpty()) {
            return "Unknown";
        }
        
        return String.format("%s %s", capitalize(firstName), capitalize(lastName)).trim();
    }
    
    /**
     * Capitalizes the first letter of a string.
     * Small, focused helper method.
     */
    private static String capitalize(String str) {
        if (str == null || str.isEmpty()) {
            return "";
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
    }
    
    /**
     * Processes a list of items.
     * Example of method with reasonable parameter count.
     */
    public static void processItems(String[] items, boolean shouldSort, int maxItems) {
        Objects.requireNonNull(items, "Items array cannot be null");
        
        // Copy array to avoid modifying input (defensive copy)
        String[] workingCopy = items.clone();
        
        if (shouldSort) {
            java.util.Arrays.sort(workingCopy);
        }
        
        // Process limited number of items
        int limit = Math.min(workingCopy.length, maxItems);
        for (int i = 0; i < limit; i++) {
            processItem(workingCopy[i]);
        }
    }
    
    /**
     * Processes a single item.
     * Extracted from loop for clarity and reusability.
     */
    private static void processItem(String item) {
        System.out.println("Processing: " + item);
        // Actual processing logic here
    }
    
    /**
     * Builder pattern for handling many parameters.
     */
    public static class UserProfileBuilder {
        private String username;
        private String email;
        private int age;
        private String country;
        
        public UserProfileBuilder setUsername(String username) {
            this.username = Objects.requireNonNull(username, "Username cannot be null");
            return this;
        }
        
        public UserProfileBuilder setEmail(String email) {
            this.email = Objects.requireNonNull(email, "Email cannot be null");
            return this;
        }
        
        public UserProfileBuilder setAge(int age) {
            if (age < 0 || age > 150) {
                throw new IllegalArgumentException("Invalid age: " + age);
            }
            this.age = age;
            return this;
        }
        
        public UserProfileBuilder setCountry(String country) {
            this.country = country; // Optional parameter
            return this;
        }
        
        public UserProfile build() {
            return new UserProfile(username, email, age, country);
        }
    }
    
    /**
     * Immutable data class.
     */
    public static class UserProfile {
        private final String username;
        private final String email;
        private final int age;
        private final String country;
        
        public UserProfile(String username, String email, int age, String country) {
            this.username = Objects.requireNonNull(username);
            this.email = Objects.requireNonNull(email);
            this.age = age;
            this.country = country; // Can be null
        }
        
        // Getter methods only (no setters - immutable)
        public String getUsername() { return username; }
        public String getEmail() { return email; }
        public int getAge() { return age; }
        public String getCountry() { return country; }
    }
    
    /**
     * Example of method that avoids deep nesting.
     */
    public static void processTransaction(Transaction tx) {
        // Early returns to avoid deep nesting
        if (tx == null) {
            System.out.println("Transaction is null");
            return;
        }
        
        if (!tx.isValid()) {
            System.out.println("Invalid transaction");
            return;
        }
        
        if (tx.getAmount() <= 0) {
            System.out.println("Amount must be positive");
            return;
        }
        
        // Main processing logic (no nesting)
        executeTransaction(tx);
        logTransaction(tx);
        sendNotification(tx);
    }
    
    // Mock classes for demonstration
    static class Transaction {
        boolean isValid() { return true; }
        double getAmount() { return 100.0; }
    }
    
    static void executeTransaction(Transaction tx) {}
    static void logTransaction(Transaction tx) {}
    static void sendNotification(Transaction tx) {}
    
    /**
     * Example of good method naming.
     */
    public static class GoodNamingExamples {
        
        // GOOD: Clear what the method does
        public double calculateTax(double income, String countryCode) {
            // Implementation
            return 0.0;
        }
        
        // GOOD: Boolean methods should read like questions
        public boolean isValidEmail(String email) {
            return email != null && email.contains("@");
        }
        
        // GOOD: Command methods should be verbs
        public void saveToDatabase(UserProfile user) {
            // Implementation
        }
        
        // GOOD: Getter methods start with "get"
        public String getUserName() {
            return "John";
        }
        
        // GOOD: Setter methods start with "set"
        public void setUserName(String name) {
            // Implementation
        }
    }
    
    /**
     * Example of method documentation (Javadoc).
     */
    public static class DocumentedMethods {
        
        /**
         * Calculates the body mass index (BMI) for a person.
         * 
         * 

BMI is calculated as weight in kilograms divided by * the square of height in meters.

* * @param weightKg the weight in kilograms (must be positive) * @param heightM the height in meters (must be positive) * @return the calculated BMI value * @throws IllegalArgumentException if weight or height is not positive * @see BMI on Wikipedia */ public static double calculateBMI(double weightKg, double heightM) { if (weightKg <= 0) { throw new IllegalArgumentException("Weight must be positive: " + weightKg); } if (heightM <= 0) { throw new IllegalArgumentException("Height must be positive: " + heightM); } return weightKg / (heightM * heightM); } /** * Determines the BMI category based on the calculated BMI value. * * @param bmi the BMI value to categorize * @return the BMI category as a string */ public static String getBMICategory(double bmi) { if (bmi < 18.5) return "Underweight"; if (bmi < 25) return "Normal weight"; if (bmi < 30) return "Overweight"; return "Obese"; } } public static void main(String[] args) { System.out.println("=== METHOD DESIGN BEST PRACTICES ===\n"); // Example 1: Using the builder pattern System.out.println("1. Builder Pattern Example:"); UserProfile user = new UserProfileBuilder() .setUsername("johndoe") .setEmail("john@example.com") .setAge(30) .setCountry("USA") .build(); System.out.println("Created user: " + user.getUsername()); System.out.println(); // Example 2: Good method naming System.out.println("2. Method Naming Examples:"); GoodNamingExamples examples = new GoodNamingExamples(); System.out.println("Is valid email? " + examples.isValidEmail("test@example.com")); System.out.println(); // Example 3: Documented methods System.out.println("3. Documented Methods:"); double bmi = DocumentedMethods.calculateBMI(70, 1.75); String category = DocumentedMethods.getBMICategory(bmi); System.out.printf("BMI: %.2f (%s)%n", bmi, category); System.out.println(); // Example 4: Error handling System.out.println("4. Proper Error Handling:"); try { calculateRectangleArea(-5, 10); } catch (IllegalArgumentException e) { System.out.println("Caught expected error: " + e.getMessage()); } System.out.println(); // Example 5: Formatting names System.out.println("5. Name Formatting:"); System.out.println("Formatted: " + formatFullName("john", "doe")); System.out.println("Formatted: " + formatFullName(null, "smith")); System.out.println("Formatted: " + formatFullName("", "")); } }