java-foundations Lesson 5 18 min read

Methods

Functional decomposition — breaking programs into reusable pieces

Reading: Reges & Stepp: Ch. 3

After this lesson, you will be able to:

  • Write static methods with parameters and return values
  • Explain the call stack and how method calls are organized in memory
  • Distinguish between void methods and value-returning methods
  • Use method overloading to create multiple versions of a method
  • Apply functional decomposition to break complex programs into small, focused methods

The Copy-Paste Problem

Imagine your program prints a decorative box of asterisks in five different places — after a menu, after game results, before instructions, at the end, and during replay. That’s 10 lines of box-printing code × 5 locations = 50 lines. Worse: if you need to change the box style, you edit in five places and inevitably miss one.

Methods solve this. Write the box-printing code once, give it a name, and call that name whenever you need it:

public static void printBox() {
    System.out.println("**********");
    System.out.println("*        *");
    System.out.println("**********");
}

Now printBox() is a single command you can use anywhere. Change the design once, and it updates everywhere.

From CSCD 110: Python’s def print_box(): does the same thing. Java adds public static void — you’ll learn what each keyword means, but for now, think of it as Java’s version of def.


Method Anatomy

public static double celsiusToFahrenheit(double celsius) {
    double result = celsius * 9.0 / 5 + 32;
    return result;
}
Part Purpose
public static Access and scope (always use these for now)
double Return type — what the method gives back
celsiusToFahrenheit Name — verb or verb phrase describing the action
(double celsius) Parameter — input the method needs
return result; Return statement — sends a value back to the caller

For methods that perform an action without returning a value, use void:

public static void greet(String name) {
    System.out.println("Hello, " + name + "!");
}
Check Your Understanding
What return type should a method have if it calculates and returns the area of a circle?
A void
B int
C double
D String
Answer: C. Circle area = π × r². Since π is irrational and the result will almost certainly have decimals, double is the correct return type. void means no return value, int would truncate the decimals.

Parameters and Arguments

Parameters are the variables in the method definition. Arguments are the values you pass when calling the method:

// celsius is the PARAMETER
public static double celsiusToFahrenheit(double celsius) {
    return celsius * 9.0 / 5 + 32;
}

// 100.0 is the ARGUMENT
double boiling = celsiusToFahrenheit(100.0);

Java passes arguments by value — the method gets a copy of the value, not the original variable. Changing the parameter inside the method doesn’t affect the caller’s variable.

public static void tryToChange(int x) {
    x = 999;  // Only changes the local copy
}

int myValue = 42;
tryToChange(myValue);
System.out.println(myValue);  // Still 42!

The Call Stack

When you call a method, Java creates a new stack frame — a block of memory for that method’s parameters and local variables. When the method returns, its frame is removed:

main()              → calls fahrenheitToMessage(212.0)
  boiling = 212.0       → calls celsiusToFahrenheit(100.0)
                              celsius = 100.0
                              return 212.0
                         → returns "Boiling point!"

Each method has its own isolated variables. Two methods can both have a variable named x without conflict — they live in different stack frames.

Check Your Understanding
What does "pass by value" mean in Java?
A The method receives a copy of the argument's value
B The method receives the original variable and can modify it
C The method receives the memory address of the variable
D The value is passed by reference for objects and by value for primitives
Answer: A. For primitives, Java always passes a copy. The method's parameter is a separate variable. Changing it inside the method has no effect on the caller's original variable.

Method Overloading

Java lets you define multiple methods with the same name but different parameter lists:

public static int max(int a, int b) {
    return (a > b) ? a : b;
}

public static double max(double a, double b) {
    return (a > b) ? a : b;
}

public static int max(int a, int b, int c) {
    return max(max(a, b), c);
}

The compiler picks the right version based on the arguments you pass. This is method overloading — same name, different signatures.

Check Your Understanding

What is the return type and parameter list of this method?
public static int add(int a, int b) { return a + b; }


Functional Decomposition

The real power of methods is decomposition — breaking a complex problem into small, named pieces. Each method should do one thing well:

public static void main(String[] args) {
    printHeader();
    double subtotal = calculateSubtotal();
    double tax = calculateTax(subtotal);
    double total = subtotal + tax;
    printReceipt(subtotal, tax, total);
}

Reading main tells you the program’s structure at a glance. Each helper method handles one part. If the tax calculation changes, you edit one method.

Rule of thumb: If a block of code does something you can describe in 2–5 words (“calculate tax”, “print header”, “validate input”), it should probably be a method.


Summary

Methods eliminate duplication, organize code, and make programs readable. Every method has a return type (or void), a name, and parameters. Java passes primitives by value. Method overloading lets you reuse names with different parameter types. Functional decomposition is the practice of breaking programs into small, focused methods.