Method Calls

This reading is supposed to be an explicit review of how method calls work in Java. Understanding method calls in detail will be crucial for understanding the topic of discussion this week, recursion.

A method in Java is a defined procedure that runs the code defined in its body. For example, suppose we had the following program (the class left out, but we include the main method).

public static void main(String[] args) {
    System.out.println("main start");
    methodExample();
    System.out.println("main end");
}

public static void methodExample() {
    System.out.println("method start");
    System.out.println("method end");
}

When we run this program, we would see the following output.

main start
method start
method end
main end

When you call a method, the program “pauses” and runs the statements inside the method you called. Once that method finishes, the program resumes execution at the method call. This is why we see "method start" and "method end" in between the output for "main start" and "main end". These semantics are so important we will highlight this in the quote below.

When you call a method in Java, the program “pauses” and executes the entirety of that method before returning to running the remaining instructions after the method call. Java keeps track of where we “left off”, so it can resume execution when the method call ends.

To be explicit, here are the set of steps performed by Java to run the earlier program.

Executing method:main
    println "main start"
    Pause method:main and call method:methodExample. Executing method:methodExample
        println "method start"
        println "method end"
    Finished executing method:methodExample, Resume method:main from where we left off
    println "main end"

In reality, println is also a method call, but we treat it as an atomic statement since the details inside println are not interesting to us right now.

Scope and Parameters

Remember that each method has its own scope (working set of variables) that it maintains for the lifetime of that method’s execution. Parameters are special variables in the method’s scope that get initialized by client supplied values. Since parameters are just special variables, this means that the same rules as variables apply (changing a variable created in one scope, won’t change a different variable in a different scope).

Here is a similar example to the one above, with a bit more complexity (indentation in the output for clarity).

public static void main(String[] args) {
    int x = 5;
    System.out.println("main start        : x=" + x);
    methodA(x, x + 2);
    int y = x + 4;
    System.out.println("main middle       : x=" + x + ", y=" + y);
    methodA(y, x + 2);
    System.out.println("main end          : x=" + x + ", y=" + y);
}

public static void methodA(int x, int z) {
    int y = x * z;
    System.out.println("  methodA start   : x=" + x + ", y=" + y + ", z=" + z);
    methodB(y);
    System.out.println("  methodA middle  : x=" + x + ", y=" + y + ", z=" + z);
    methodB(y);
    System.out.println("  methodA end     : x=" + x + ", y=" + y + ", z=" + z);
}

public static void methodB(int y) {
    System.out.println("    methodB start : y=" + y);
    y = 4;
    System.out.println("    methodB end   : y=" + y);
}
This method is complex enough to make it tedious to trace through, so we have provided the output below. The first key point is that the same “pausing” and “resuming” behavior is still there. The other key point is to consider that the variables in the “caller” methods do not change even if the callee method makes its own variables with the same name or reassigns its parameters. The idea here is that even though the various methods have variables (or parameters) with the same name, each method has its own set of variables that are independent of the others.

main start        : x=5
  methodA start   : x=5, y=35, z=7
    methodB start : y=35
    methodB end   : y=4
  methodA middle  : x=5, y=35, z=7
    methodB start : y=35
    methodB end   : y=4
  methodA end     : x=5, y=35, z=7
main middle       : x=5, y=9
  methodA start   : x=9, y=63, z=7
    methodB start : y=63
    methodB end   : y=4
  methodA middle  : x=9, y=63, z=7
    methodB start : y=63
    methodB end   : y=4
  methodA end     : x=9, y=63, z=7
main end          : x=5, y=9

(A Preview) Recursion

As a brief introduction to what we will be doing next week, we will be investigating what happens if a method calls itself! While this seems like a radically different concept, there are no different rules than the ones shown in the examples above. It’s okay if this example doesn’t make sense right now, as this is the topic of study for the next week; we just wanted to briefly show the pattern in the context of this reading.

public static void main(String[] args) {
    countDown(5);
}

// prints the numbers from n to 1, one on each line
public static void countDown(int n) {
    if (n >= 1) {
        System.out.println(n);
        countDown(n - 1);
    }
}
The output of this method is shown below:

5
4
3
2
1

This follows exactly from the mechanics we have described in the earlier examples in this reading. You can even try tracing it yourself using the tracing strategy from the first example! If this doesn’t make sense how this works yet, we will talk about it in class!