Defining Methods, Parameters, and Stack Diagrams

Writing your own methods, following the flow of execution as it detours from one to another, passing arguments into parameters, and drawing the stack diagram that shows which variables exist and where. Follows Think Java 2e, Chapter 4 (Methods and Testing), Sections 4.1-4.5, pp. 51-58, cross-referenced against The Java Tutorials — Defining Methods.

Subject: Java · 65 slides · code lesson

Open the interactive version of this deck

What this lesson covers

The lesson, slide by slide

1. Defining Methods, Parameters, and Stack Diagrams

Title

Think Java 2e · Chapter 4 · Methods and Testing

Sections 4.1-4.5 · pp. 51-58

2. What you will be able to do

Objectives

This lesson follows Think Java 2e, Chapter 4 (Methods and Testing), Sections 4.1-4.5, pp. 51-58. Everything on these slides can be checked against those pages.

1. Define a void method and invoke it from main.

2. Trace the flow of execution through several methods, and say why it is not the order they appear in the file.

3. Give three reasons to write a method rather than putting everything in main.

4. Distinguish an argument from a parameter, and describe parameter passing as an assignment.

5. Explain why a variable in one method is invisible in another, and what 'local variable' means.

6. Draw a stack diagram with a frame per running method, and use it to reason about scope.

3. Retrieve before you read

Warm-up

You have been calling methods since Chapter 1 without writing one.

Discussion prompt

Name three methods you have already used. For each, say whether it gives you a value back or just does something. What is the difference, in the code you write around it?

Hint: Compare System.out.println(x); with int n = in.nextInt();

Answer:

println and print do something and give nothing back — you call them on a line of their own. nextInt, nextDouble and nextLine return a value, so you have to catch it: int n = in.nextInt();

That distinction is exactly what this chapter formalises. Methods that carry out actions without returning a result are declared void; methods that return something declare the type they return. This lesson covers the first kind, and Lesson 4b covers the second.

4. Why a program has more than one method

Concept

Every program so far has had exactly one method, main. This chapter shows how to organise a program into several — which lets you name a block of statements, avoid repeating yourself, and test parts of a program separately.

Figure (svg): Three boxes giving reasons to write a method: naming a block, removing repetition, and breaking a problem into subproblems

Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 4 (Methods and Testing), Sections 4.1-4.5, pp. 51-58 — Chapter 4 opens on printed page 51.

5. Defining new methods

Section

Section 4.1

6. void means the method returns nothing

Concept

Some methods perform a computation and return a result — nextDouble reads input and returns a double. Others, like println, carry out a sequence of actions without returning anything. Java uses the keyword void to define such a method.

public static void newLine() {
    System.out.println();
}

public static void main(String[] args) {
    System.out.println("First line.");
    newLine();
    System.out.println("Second line.");
}
part of the headerwhat it says
publicthe method can be invoked from other classes
staticit belongs to the class rather than to an object — Chapter 10
voidit does not return a result
newLineits name, which you choose
()it takes no parameters

System.out.println() with no argument displays a blank line — which is the whole job of newLine. The output of this program is First line., a blank line, then Second line.

7. The shape of a method definition

Notation

You have been reading this header since Chapter 1 without being able to change it. Now every part of it is a decision you make.

Annotate

  • public — the method can be invoked from other classes. Both newLine and main are public in this example.
  • static — still boilerplate for now, as it was in Chapter 1. It means the method belongs to the class itself rather than to an object; Chapter 10 makes that meaningful.
  • void — this method returns no result, in contrast to something like nextDouble. Lesson 4b replaces this word with a real type.
  • The name. By convention method names begin with a lowercase letter and use camel case — jammingWordsTogetherLikeThis. You can use any name except main or a Java keyword.
  • The parentheses hold the parameters. Empty here, because newLine needs no information to do its job.
  • Remember Java is case-sensitive. In this example the class is NewLine and the method is newLine — different names, and the capitalisation is what tells a reader which is which.

Only the last three parts vary from method to method in this chapter. public static will be on the front of every method you write until Chapter 11.

8. Building threeLine from newLine

Worked example

Once a method exists you can call it from another method — including one you also wrote. Build up three blank lines from one.

public class NewLine {

    public static void newLine() {
        System.out.println();
    }

    public static void threeLine() {
        newLine();
        newLine();
        newLine();
    }

    public static void main(String[] args) {
        System.out.println("First line.");
        threeLine();
        System.out.println("Second line.");
    }
}
methodwhat it doescalls
newLinedisplays one blank lineprintln
threeLinedisplays three blank linesnewLine, three times
mainprints two lines with a gap betweenprintln, threeLine, println

Write the smallest method first.

Why: newLine does one thing, and it is the thing you will reuse.

Build the larger method out of the smaller one.

Why: threeLine calls newLine three times rather than calling println three times — so if the definition of 'a blank line' ever changes, one place changes.

Call it from main.

Why: threeLine(); on a line of its own, because it is a void method and there is no value to catch.

Note what the class now contains.

Why: NewLine contains three methods: newLine, threeLine and main. A class, for now, is a collection of methods.

Verify: Run it and expect First line., three blank lines, then Second line.

Why: If you got only one blank line, check that threeLine is actually being called rather than newLine — the two names differ by more than their length, and this is exactly the kind of slip a good method name prevents.

9. What does this program display?

Prediction

Read the calls, not the order of the definitions.

public class NewLine {

    public static void newLine() {
        System.out.println();
    }

    public static void threeLine() {
        newLine();
        newLine();
        newLine();
    }

    public static void main(String[] args) {
        System.out.println("First line.");
        threeLine();
        System.out.println("Second line.");
    }
}
statement in mainoutput
println("First line.")First line.
threeLine()three blank lines
println("Second line.")Second line.

Predict first

How many lines of output are there in total?

  • 5
  • 2
  • 3
  • 4

Correct: 5

Why: One line of text, then three blank lines from threeLine, then a second line of text — five lines altogether. Blank lines are still lines: System.out.println() with no argument outputs nothing followed by a newline, which is exactly what makes the gap.

10. Naming a method well

Concept

A method's name is the only documentation most readers will look at. The conventions are worth following because the entire Java library follows them.

conventionexamplewhy
begins with a lowercase letternewLine, printTwicedistinguishes it from a class name
camel case for later wordsprintTime, calculateAreareadable without spaces, which are not allowed
usually a verb or verb phraseprint, calculate, converta method does something
not main, not a keyword—main is reserved as the entry point

The naming set is now complete: UpperCamelCase for classes, lowerCamelCase for methods and variables, ALL_CAPS for constants. None of it is enforced by the compiler and all of it is relied on by readers.

11. Defining a method inside another method

Trap

The trap

The mistake. Putting the new method inside main, where the statements go.

public static void main(String[] args) {
    public static void newLine() {   // illegal here
        System.out.println();
    }
    newLine();
}
what is inside whatlegal?
a method inside a classyes — this is how it works
a statement inside a methodyes
a method inside a methodno — illegal start of expression

The nesting from Lesson 3a decides this: a class contains methods, and a method contains statements. A method is not a statement, so it cannot go where statements go.

The fix

Methods are siblings inside the class, not nested in one another.

public class NewLine {

    public static void newLine() {
        System.out.println();
    }

    public static void main(String[] args) {
        newLine();
    }
}
levelcontains
class NewLinenewLine and main — side by side
method newLineone statement
method mainone statement, which invokes newLine

The order the methods appear in the file does not matter at all — which is the subject of the next section, and surprises most people the first time.

12. Class, method, or statement?

Definition probe

The nesting from Lesson 3a, applied to a real file.

Sort into buckets

Sort each line of the NewLine program.

a class definition
public class NewLine {
a method definition
public static void newLine() {; public static void main(String[] args) {
a statement
System.out.println();; threeLine();
cls
Opens the class, which is the container for everything else in the file.
mth
Opens a method definition. Note that all three definitions sit side by side inside the class, never inside one another.
stm
A line of code that performs one action, ending in a semicolon. Invoking a void method is a statement in its own right.

13. Define a method that prints a separator

Fill the middle

A void method with no parameters.

Fill in the blanks

public static void separator() separator()

public static void main(String[] args) ___};
}

Why: void says the method returns no result, which is right for something that only prints. Invoking it is separator() — with the parentheses, because without them you have named the method rather than calling it, and on a line of its own, because there is no value to catch.

14. Why write newLine at all?

Explain it to yourself

It is one line long and its body is shorter than its name.

Discussion prompt

newLine contains a single statement, System.out.println();. Writing the method is more typing than just calling println. Give the strongest argument you can for defining it anyway.

Hint: Think about what the two versions say to a reader.

Answer:

The strongest argument is naming. System.out.println(); with no argument looks like a mistake — a reader wonders whether something was left out. newLine(); says exactly what is intended.

The second argument is that it gives you something to build on. threeLine is written in terms of newLine, so the concept 'a blank line' exists in one place. This is the smallest possible example of the technique the whole chapter is about: name a block of statements so you can compose with it.

15. The flow of execution

Section

Section 4.2

16. Execution is a series of detours

Concept

When you look at a class containing several methods, it is tempting to read it top to bottom. But that is not the flow of execution — the order the program actually runs. The NewLine program runs its methods in the opposite order to the one they are listed in.

Figure (svg): A pipeline showing execution entering main, detouring into threeLine, then into newLine, and returning each time

One method can invoke another, so the detours nest. Java keeps track: when println finishes it returns to newLine, when newLine finishes it returns to threeLine, and when threeLine finishes it returns to main.

17. The order of the file against the order of execution

Picture it

The NewLine program lists newLine first and main last, and runs them in the reverse order. Reading a file top to bottom tells you what exists, not what happens.

Figure (svg): Two panels comparing the order methods appear in the file against the order they execute

This is why a program's structure has to be traced rather than read. The file is a list of definitions; the execution is a path through them.

18. Tracing the detours

Worked example

Follow the flow of execution through NewLine, statement by statement, noting each jump and each return.

public static void main(String[] args) {
    System.out.println("First line.");
    threeLine();
    System.out.println("Second line.");
}
stepwhere we arewhat happens
1mainprintln("First line.") — output appears
2mainthreeLine() — jump away
3threeLinenewLine() — jump again
4newLineprintln() — a blank line, then return
5threeLinenewLine() twice more, each returning
6mainback where we left off
7mainprintln("Second line.")

Start at the first statement of main.

Why: Always, regardless of where main appears in the file.

At an invocation, jump to the method's first line.

Why: Everything after the call in the current method waits.

Run all the statements there.

Why: Including any further invocations, which nest the same way.

Return to exactly where you left off.

Why: Not to the start of the calling method — to the statement after the call.

Verify: Count the outputs: one line, three blanks, one line — five in total, in that order.

Why: If your trace produced the two text lines adjacent, you returned to the wrong place after threeLine. The return goes to the statement AFTER the call, which is what makes the gap appear between them.

19. Order the flow of execution

Ranking

For the NewLine program, from the moment the program starts.

Put in order

  1. main begins
  2. println("First line.") runs
  3. threeLine is invoked
  4. newLine runs, three times
  5. control returns to main
  6. println("Second line.") runs

Why: Execution always begins at main's first statement, and each invocation is a detour that returns to the statement immediately after the call. The step people misplace is the return: control comes back to where it left off in main, not to the beginning of main.

20. Four reasons to write another method

Concept

Beginners often wonder why it is worth writing other methods when everything could go in main. The NewLine example demonstrates several reasons.

reasonin NewLinein general
naming a blocknewLine() says what println() with no argument meanscode that explains itself needs fewer comments
removing repetitionthreeLine calls newLine three timesnine newlines would be threeLine three times
breaking down a problemone method per subproblemyou can focus on each part in isolation
testing separatelynewLine can be checked on its owna complex program is easier to get working when each part is known good

Think Java calls the last one perhaps the most important: organising code into multiple methods lets you test individual parts of your program separately. That idea drives the whole of Lesson 4b's incremental development.

21. Assuming the file order is the run order

Trap

The trap

The misreading. Methods run in the order they appear, so newLine runs first.

public class NewLine {
    public static void newLine() { ... }      // 1st in the file
    public static void threeLine() { ... }    // 2nd
    public static void main(String[] args) {  // 3rd
        System.out.println("First line.");
        threeLine();
    }
}
prediction from file orderwhat actually happens
newLine runs first — a blank linenothing runs until main starts
then threeLinemain's first statement runs first
then mainthe others run only when invoked

A method definition does not do anything. It says what would happen if the method were invoked. Nothing in the file runs until main is entered.

The fix

Execution begins at main, and methods run only when invoked.

// The file can list them in any order at all:
public class NewLine {
    public static void main(String[] args) { ... }   // first in the file
    public static void threeLine() { ... }
    public static void newLine() { ... }
}
// The program behaves identically.
what decidesthe order of execution
where main is in the fileno effect
where the other methods areno effect
which statements invoke which methodsthis is what decides it

So the order of methods in a file is purely a readability decision. Many programmers put main first so a reader meets the top-level story before the details; Think Java's examples usually put it last.

22. Does moving main change the output?

Prediction

The three method definitions are reordered in the file; nothing else changes.

Predict first

If you move main to the top of the file, above newLine and threeLine, what happens?

  • Nothing — the output is identical
  • The program will not compile
  • The blank lines appear before the first line of text
  • newLine runs before main

Correct: Nothing — the output is identical

Why: Programs always begin at the first statement of main regardless of where main is in the source file, and every other method runs only when it is invoked. The order of definitions in a file is a readability decision with no effect whatsoever on behaviour.

23. Watch the flow of execution

Invariant

Step through the detours and watch which method is running at each moment.

Step through it

At the third frame, how many methods have started but not yet finished?

  1. main is running. Nothing else exists yet.
  2. main pauses at the call. threeLine begins.
  3. threeLine pauses. newLine begins.
  4. newLine finishes and control returns to threeLine, at the statement after the call.
  5. threeLine finishes and control returns to main, at the statement after the call.
  6. main continues from exactly where it left off, and prints the second line.

Three — main, threeLine and newLine. Each is waiting for the one it called. That stack of waiting methods is exactly what the next section draws.

24. What is Java keeping track of?

Socratic

The detours nest three deep and come back in the right order every time.

Discussion prompt

When println finishes, Java returns to newLine; when newLine finishes, to threeLine; then to main. What information must Java be storing to get this right, and where do you think it keeps it?

Hint: It has to remember more than one place at once.

Answer:

It must remember, for every method that has started but not finished, which statement to come back to — and it must remember them in order, because the most recently started method is the first to finish.

That is a stack: last in, first out. Java keeps it in a region of memory called the call stack, and the next section draws it. This is also why a runaway recursion in Chapter 8 produces a StackOverflowError — the store of waiting methods is finite.

25. Parameters and arguments

Section

Section 4.3

26. You provide arguments; the method names parameters

Concept

When you invoke a method, you provide the arguments. When you define a method, you name the parameters — variables that indicate what arguments are required.

public class PrintTwice {

    public static void printTwice(String s) {
        System.out.println(s);
        System.out.println(s);
    }

    public static void main(String[] args) {
        printTwice("Don't make me say this twice!");
    }
}
termwhere it appearsin this example
parameterin the method definitionString s
argumentat the call site"Don't make me say this twice!"
parameter passingjust before the method runsthe argument is assigned to s

argument — A value you provide when you call a method. It must have the type the method expects.

parameter — A variable named in a method definition, holding a value the method requires before it can run.

parameter passing — The process of assigning an argument value to a parameter variable.

Before the method executes, the argument gets assigned to the parameter. The process is called parameter passing, because the value is passed from outside the method to the inside.

27. An argument is assigned to a parameter

Picture it

Parameter passing is just an assignment — the same operation from Chapter 2, happening automatically at the moment of the call.

Figure (svg): A trace strip showing an argument value being assigned to a parameter variable just before the method body runs

An argument can be any kind of expression, not just a literal. If you have a String variable you can use its value as an argument — and Java evaluates the expression first, then assigns the result.

28. Reading a type-mismatch error

Worked example

The value you provide as an argument must have the same, or a compatible, type as the parameter. Getting it wrong produces a long message that is more helpful than it first looks.

printTwice(17);   // syntax error
line of the messagewhat it tells you
method printTwice cannot be applied to given typesthe call does not match any definition
required: java.lang.Stringwhat the parameter is
found: intwhat you passed
actual argument int cannot be converted to Stringand Java will not convert it for you

Read required and found first.

Why: Those two lines are the whole diagnosis: a String was needed, an int was supplied.

Note that Java will not convert here.

Why: It will not turn the integer 17 into the string "17" automatically, even though + would.

Contrast with a case where it does convert.

Why: Math.sqrt requires a double, but Math.sqrt(25) works — the int 25 is converted to 25.0, because nothing is lost.

Fix by supplying the right type.

Why: printTwice("17"); passes a String.

Verify: Change the argument to "17" and confirm it compiles and prints the text twice.

Why: The rule to carry away is the same one from Lesson 2b: widening conversions happen automatically, others do not. int to double is silent; int to String is not a conversion at all.

29. Argument or parameter?

Definition probe

The distinction is about which side of the call you are on.

Sort into buckets

Sort each item from the printTwice example.

a parameter
String s in the method header; int hour in printTime(int hour, int minute)
an argument
"Don't make me say this twice!" in main; message passed in printTwice(message)
par
Named in the method DEFINITION. It is a variable that will receive a value before the method runs.
arg
Supplied at the CALL. It is a value — or an expression producing one — passed from outside the method to the inside.

30. Local variables live only in their own method

Concept

Parameters and other variables exist only inside the methods where they are defined. That is why they are called local variables.

public static void printTwice(String s) {
    System.out.println(s);        // s exists here
}

public static void main(String[] args) {
    String message = "Never say never.";
    printTwice(message);          // message exists here
    // System.out.println(s);     // error: cannot find symbol
}
variableexists invisible in main?visible in printTwice?
sprintTwicenoyes
messagemainyesno
argsmainyesno

local variable — A variable declared inside a method. It cannot be accessed from outside that method.

In the printTwice example there is no such thing as s in main, and no such thing as message inside printTwice. Trying to use either in the wrong place is a compile error — cannot find symbol.

31. Declaring the types of your arguments

Trap

The trap

A very common beginner mistake, and the error message does not obviously explain it.

int hour = 11;
int minute = 59;
printTime(int hour, int minute);   // syntax error
what the compiler seeswhat it expected
int hour — a variable declarationan expression representing a value
int minute — another declarationanother expression
a declaration inside a call's parenthesesillegal

The compiler reads int hour as a declaration rather than as a value. You would never write printTime(int 11, int 59); — and this is the same mistake with variables in place of the literals.

The fix

Types go in the definition; values go at the call.

// definition — types named here:
public static void printTime(int hour, int minute) { ... }

// call — values only:
int hour = 11;
int minute = 59;
printTime(hour, minute);
wherewhat you write
method definitiona type and a name for each parameter
method callan expression for each argument — and nothing else

One way to remember it: the definition is a promise about what is required; the call is the delivery. You state the type once, where the promise is made.

32. Which call compiles?

Prediction

The parameter is a String.

public static void printTwice(String s) { ... }

// which of these is legal?
printTwice("17");
printTwice(17);
callargument typeparameter typelegal?
printTwice("17")StringStringyes
printTwice(17)intStringno

Predict first

Which of the two calls compiles?

  • Only printTwice("17")
  • Only printTwice(17)
  • Both
  • Neither

Correct: Only printTwice("17")

Why: The argument must have the same or a compatible type as the parameter, and Java will not convert the integer 17 to the string "17" automatically. Note that it WOULD convert an int to a double — Math.sqrt(25) works — because that conversion loses nothing. int to String is not a widening conversion; it is a different kind of change entirely.

33. Where does each variable exist?

Error analysis

A student added print statements to check their values, and two of the four lines will not compile.

Annotate

  • Line 1 is fine. s is printTwice's parameter, so it exists throughout printTwice's body.
  • Line 2 does not compile. message is a local variable of main. Inside printTwice there is no such thing, and the compiler says cannot find symbol.
  • Line 3 is fine. message is declared in main and used in main.
  • Line 4 does not compile. s belongs to printTwice. In main there is no such thing.
  • The principle: variables exist only inside the method where they are defined, which is why they are called local variables. The only value that crosses the boundary is the one passed as an argument.

This isolation is a feature rather than a restriction. It means you can read a method and know that nothing outside it can interfere with its variables.

34. Define and call a method with a parameter

Fill the middle

A method that greets a named person.

Fill in the blanks

public static void greet(String name) "Ada"

public static void main(String[] args) ___});
}

Why: The parameter needs a type and a name in the definition — String name — and the call supplies a value of that type with no type written. Writing greet(String "Ada") would be a syntax error, because the compiler would read it as a declaration rather than a value.

35. Multiple parameters

Section

Section 4.4

36. Two parameters, two arguments, matched in order

Concept

A method can take any number of parameters. To invoke it you provide the same number of arguments, of compatible types, in the same order.

public class PrintTime {

    public static void printTime(int hour, int minute) {
        System.out.print(hour);
        System.out.print(":");
        System.out.println(minute);
    }

    public static void main(String[] args) {
        int hour = 11;
        int minute = 59;
        printTime(hour, minute);
    }
}
parameterreceivesvalue
hour (in printTime)the first argument11
minute (in printTime)the second argument59
output—11:59

printTime has two parameters named hour and minute. And main has two variables also named hour and minute. Although they have the same names, these are not the same variables — and that is the point of this section.

37. Same name, different variable

Picture it

The hour in printTime and the hour in main refer to different memory locations, and they can hold different values.

Figure (svg): A stack diagram with main on top holding hour 11 and minute 59, and printTime below holding hour 12 and minute 0

This is the picture after printTime(hour + 1, 0). Java evaluated the arguments first — giving 12 and 0 — and assigned those to the parameters. Inside printTime, hour is 12; in main it is still 11.

38. Arguments are evaluated before they are passed

Worked example

An argument can be any expression. Java works out its value first, and only then assigns it to the parameter.

int hour = 11;
int minute = 59;
printTime(hour + 1, 0);
stepin mainin printTime
before the callhour = 11, minute = 59does not exist yet
evaluate the argumentshour + 1 is 12; 0 is 0—
assign to the parametershour = 11, minute = 59hour = 12, minute = 0
the body runsunchangedprints 12:0
after the returnhour = 11, minute = 59gone

Evaluate each argument expression.

Why: hour + 1 uses main's hour, which is 11, giving 12.

Assign the results to the parameters, in order.

Why: The first argument goes to the first parameter.

Note that main's variables are untouched.

Why: Adding one to hour in the argument did not change main's hour.

And that changes inside the method do not escape.

Why: If printTime modified one of its parameters, that change would have no effect on the variables in main.

Verify: Print main's hour after the call and expect 11, not 12.

Why: That is the whole content of this section: the two hour variables share a name and nothing else. The name is a coincidence of your choosing, and Java attaches no meaning to it.

39. What does main print?

Prediction

The method modifies its parameter.

public static void bump(int x) {
    x = x + 10;
}

public static void main(String[] args) {
    int x = 1;
    bump(x);
    System.out.println(x);
}
stepmain's xbump's x
int x = 1;1—
bump(x) — parameter passing11
x = x + 10 inside bump111
after the return1gone

Predict first

What is displayed?

  • 1
  • 11
  • 10
  • a compile error, because both are called x

Correct: 1

Why: Parameter passing assigns a copy of the argument's value to the parameter, so bump's x is a different variable that happens to share a name. Changing it has no effect on main's x. Sharing the name is perfectly legal — the two variables are in different methods and cannot see each other.

40. Why sharing a name is allowed at all

Concept

It might seem safer if Java forbade two methods from using the same variable name. It does not, and the reason is worth understanding.

if names had to be unique across methodsas it actually is
you would have to know every other method's namesyou only have to know your own
adding a method could break an existing onemethods cannot interfere with each other
a good name could only be used once in a programhour means hour wherever it is natural
large programs would become impossiblemethods stay independent however many there are

Locality is what makes methods composable. Because a method's variables are invisible outside it, you can read one method and be sure that nothing elsewhere is quietly changing its values. That guarantee is why the same name in two methods is not merely tolerated but normal.

41. Expecting a method to change the caller's variable

Trap

The trap

The assumption. The parameter and the argument variable are somehow linked.

public static void addOne(int n) {
    n = n + 1;
}

public static void main(String[] args) {
    int count = 5;
    addOne(count);
    System.out.println(count);   // predicted 6
}
stepcount (in main)n (in addOne)
before the call5—
parameter passing55 — a copy
n = n + 156
after the return5gone

The output is 5. This is Chapter 2's b = a lesson again in a new setting: assignment copies a value. Parameter passing is an assignment, so the parameter gets a copy.

The fix

To get a value out of a method, return it — which is Lesson 4b.

public static int addOne(int n) {
    return n + 1;
}

public static void main(String[] args) {
    int count = 5;
    count = addOne(count);
    System.out.println(count);   // 6
}
stepcountwhat changed it
int count = 5;5the declaration
count = addOne(count);6the assignment in MAIN, using the returned value

Notice where the change happens: in main, on the line with the =. The method computed a value; only the caller decided to store it. That separation is what makes methods safe to call.

42. Which calls match this definition?

Discrimination

public static void printTime(int hour, int minute)

Sort into buckets

Sort each call by whether it compiles.

compiles
printTime(11, 59);; printTime(hour, minute);; printTime(hour + 1, 0);
does not compile
printTime(int hour, int minute);; printTime("11", "59");
ok
Two arguments, each an expression whose value is an int. Literals, variables and arithmetic expressions are all fine — Java evaluates them first and assigns the results to the parameters.
no
Either the arguments are declarations rather than values, or their types do not match the parameters. Types are written once, in the definition, and Java will not convert a String to an int.

43. Trace two frames at once

Pattern

Step through printTime(hour + 1, 0) and watch both methods' variables.

Step through it

At the third frame, how many variables called hour exist?

  1. Only main exists. Its hour is 11.
  2. Java evaluates hour + 1 using MAIN's hour, giving 12. Nothing is assigned yet.
  3. A new frame for printTime holds its own hour and minute. main's values are untouched.
  4. printTime's frame is gone, along with its variables. main's hour is still 11.

Two — one in each frame, holding 11 and 12. They share a name and nothing else, which is exactly what the stack diagram is drawn to make visible.

44. When would you WANT a method to change the caller's variable?

Counterexample

You have just been told it cannot. Ask whether that is always what you want.

Discussion prompt

A method cannot change its caller's variables. Think of a task where that restriction is inconvenient, and suggest how you would work around it with what you know so far.

Hint: What if the method needs to produce two values?

Answer:

The obvious case is a method that should produce more than one result — splitting inches into feet and remaining inches, say. Returning one value handles one of them.

With what you know now, the workaround is to write two methods, or to return one value and recompute the other. Neither is elegant.

The real answer comes in Chapter 10: objects can be modified by the methods they are passed to, because what gets copied is a reference rather than the value itself. That is precisely why Chapters 9 and 10 spend so long on the difference between primitives and objects — the rule you have just learned holds for primitives and is exactly what changes for objects.

45. Stack diagrams

Section

Section 4.5

46. A frame for every running method

Concept

One way to keep track of variables is to draw a stack diagram — a memory diagram that shows the currently running methods. For each method there is a box, called a frame, containing that method's parameters and local variables. The method's name appears outside the frame; the variables inside.

Figure (svg): A stack diagram with main at the top holding hour and minute, and printTime below with its own hour and minute

stack diagram — A graphical representation of the variables belonging to each method, with the calls stacked in the flow of execution.

frame — In a stack diagram, the box holding one method's parameters and local variables, with their current values.

scope — The area of a program where a variable can be used.

Think Java draws main on top, because it executed first, with each called method below it. Every stack diagram in this course follows that convention.

47. Three frames, three deep

Picture it

The NewLine program at the moment newLine is running: three methods have started and none has finished.

Figure (svg): A stack diagram with main at the top, threeLine below it, and newLine at the bottom, each with its own frame

None of these frames has any interesting variables, which makes the shape easy to see: the depth of the stack is how many methods are waiting. When newLine returns its frame disappears, then threeLine's, and main is alone again.

48. Drawing a stack diagram at a chosen moment

Worked example

Like memory diagrams, stack diagrams show a particular point in time. Pick the moment first, then draw only what exists then.

public static void printTime(int hour, int minute) {
    System.out.print(hour);      // <- draw the diagram here
    System.out.print(":");
    System.out.println(minute);
}

public static void main(String[] args) {
    int hour = 11;
    int minute = 59;
    printTime(hour + 1, 0);
}
framevariablevalue
mainargsthe command-line arguments
mainhour11
mainminute59
printTimehour12
printTimeminute0

Choose the moment and write it down.

Why: Here: the first statement of printTime, just before anything is printed.

Draw a frame for every method that has started and not finished.

Why: main and printTime — two frames, main on top.

Put each method's parameters and local variables inside its own frame.

Why: main has args, hour and minute; printTime has its own hour and minute.

Fill in the current values.

Why: printTime's hour is 12, because the argument hour + 1 was evaluated before the call.

Verify: Check that no variable appears in two frames unless it genuinely exists in both — here hour does, with different values.

Why: And check the depth against the flow of execution: two methods have started and neither has returned, so there are exactly two frames.

49. How many frames?

Prediction

The NewLine program, at the moment println is executing inside newLine.

Predict first

How many frames are on the stack (counting only methods you wrote)?

  • 3 — main, threeLine, newLine
  • 1 — only newLine
  • 2 — main and newLine
  • 4

Correct: 3 — main, threeLine, newLine

Why: main called threeLine, which called newLine, and none of them has finished — so all three have frames. println would add a fourth, but it belongs to the Java library rather than to your program. The depth of the stack is exactly the number of methods that have started and not yet returned.

50. What a stack diagram is good for

Concept

Stack diagrams help you visualise the scope of a variable — the area of a program where it can be used — and they are a good mental model for how variables and methods work at run time.

questionhow the diagram answers it
Can this method see that variable?only if it is in this method's own frame
Why is there a cannot find symbol error?the name is in a different frame
Which hour does this line mean?the one in the frame you are currently inside
How deep is the program?the number of frames
What happens when this method returns?its frame disappears, taking its variables with it

Learning to trace execution on paper or a whiteboard is a useful skill for communicating with other programmers. Tools can draw these for you too — Java Tutor lets you step through a program forwards and backwards and watch the frames appear and disappear.

51. Drawing a frame for a method that has already returned

Trap

The trap

The mistake. Drawing every method that has run, rather than every method still running.

public static void main(String[] args) {
    newLine();
    threeLine();     // <- draw the diagram here, inside threeLine
}
methodhas it run?is it still running?does it get a frame?
mainyesyes — waitingyes
newLine (first call)yesno — it returnedno
threeLineyesyesyes

The first newLine() finished before threeLine() started. Its frame — and its variables — are gone. Drawing it suggests its variables are still accessible, which is exactly the wrong idea.

The fix

A frame exists only while its method is running.

// at the marked moment, exactly two methods have started
// and not finished: main, and threeLine.
framewhy it is there
mainstarted, and waiting for threeLine to return
threeLinecurrently running

When a method returns, its frame is removed and its local variables cease to exist. That is why you cannot 'look at' a variable after its method has finished — there is nothing left to look at, which is also why a method must return a value if the caller is to keep it.

52. In scope or not?

Definition probe

Using the printTime stack diagram: main has hour, minute and args; printTime has hour and minute.

Sort into buckets

Sort each usage by whether it compiles.

in scope — compiles
reading hour inside printTime; reading minute inside main
out of scope — cannot find symbol
reading args inside printTime; reading printTime's hour from main
yes
The variable is in the frame of the method doing the reading, so it is in scope there.
no
The variable lives in a different method's frame. Local variables cannot be accessed from outside the method that declares them, whichever direction you try.

53. Watch frames appear and disappear

Invariant

Step through the NewLine program and watch the stack grow and shrink.

Step through it

What is the maximum number of frames this program ever has?

  1. main is running. One frame.
  2. main called threeLine. main waits; two frames.
  3. threeLine called newLine. Three frames — the deepest this program goes.
  4. newLine returned. Its frame is gone, along with any variables it had.
  5. threeLine returned too. main resumes with one frame, exactly as it started.

Three. The stack grows on every call and shrinks on every return, and its depth at any moment is the number of methods waiting. Chapter 8's recursion is what happens when that depth becomes large.

54. Explain scope using the diagram

Explain it

Draw before you speak.

Discussion prompt

A classmate has written System.out.println(s); in main, where s is printTwice's parameter, and cannot understand the cannot find symbol error. Draw the stack diagram and use it to explain. Then answer their follow-up: 'so how DO I get the value out of the method?'

Hint: The second question is what the next lesson is about.

Answer:

Draw two frames: main on top with its own variables, printTwice below with s inside it. s is in printTwice's frame, so it exists only while printTwice is running and only inside that method. From main there is no s to find — which is precisely what the compiler said.

And to get a value out: the method has to return it, and main has to catch it in a variable. That is the only way anything crosses the boundary in that direction — an argument goes in, a return value comes out.

If your explanation reached for the diagram first and the words second, you have the right habit. Scope is a spatial idea, and the picture does most of the work.

55. Arguments, parameters, and local variables

Comparison

Three things that are easy to blur. Fill the blanks.

Comparison matrix

argumentparameterother local variable
written whereat the callin the method definition's parenthesesinside the method body
has a type written?no — just a valueyesyes, in its declaration
who gives it a valueyou, when callingparameter passing, automaticallyan assignment you write
how long it existsonly until the call is madewhile the method runswhile the method runs

The bottom row is what a stack diagram draws: everything in a frame lives exactly as long as that frame, and vanishes when the method returns.

56. The pattern to carry away

Pattern

A method call is three separate events, and keeping them apart resolves nearly every confusion in this lesson.

eventwhat happenswhere
1. evaluate the argumentseach expression becomes a valuein the calling method
2. pass the parameterseach value is ASSIGNED to a parametera new frame is created
3. run and returnthe body executes, then the frame is discardedin the called method

57. Check: the flow of execution

Check

Work it out before you click.

public class Order {
    public static void b() { System.out.println("B"); }
    public static void a() { System.out.println("A"); b(); }
    public static void main(String[] args) {
        System.out.println("start");
        a();
        System.out.println("end");
    }
}
stepoutput
main's first printlnstart
a() is invoked; its println runsA
b() is invoked from aB
control returns to mainend

Check your understanding

What does this program display?

  • A. start, A, B, end (correct)
  • B. B, A, start, end
  • C. start, end, A, B
  • D. start, A, end, B

Answer: A

Why: Execution begins at main regardless of where the methods appear in the file. main prints 'start', then calls a, which prints 'A' and calls b, which prints 'B'; control then returns to main, which prints 'end'.

Why B tempts people
This follows the order the methods appear in the file, but a definition does nothing until it is invoked.
Why C tempts people
This assumes main finishes before the called methods run, but a call is a detour — main pauses until it returns.
Why D tempts people
This has b running after main finishes, but b is called from within a, so it completes before a returns.

58. Check: parameter passing

Check

Work it out before you click.

public static void triple(int n) {
    n = n * 3;
    System.out.println(n);
}

public static void main(String[] args) {
    int n = 4;
    triple(n);
    System.out.println(n);
}
stepmain's ntriple's n
int n = 4;4—
triple(n)44
n = n * 3 inside triple412
after the return4gone

Check your understanding

What does this program display?

  • A. 12 then 4 (correct)
  • B. 12 then 12
  • C. 4 then 12
  • D. 4 then 4

Answer: A

Why: triple prints its own n after tripling it, giving 12; main then prints its own n, which was never changed, giving 4. Parameter passing assigns a copy of the value, so the two variables share a name and nothing else.

Why B tempts people
This assumes changing the parameter changes the caller's variable, but the parameter received a copy.
Why C tempts people
This has the two printlns in the wrong order — triple's runs first, because the call completes before main continues.
Why D tempts people
This misses that triple really does change its OWN n before printing it.

59. Check: scope

Check

Work it out before you click.

public static void show(String label) {
    int count = 3;
    System.out.println(label + count);
}

public static void main(String[] args) {
    show("n = ");
    System.out.println(count);
}
variabledeclared inused inlegal?
labelshow's parametersshowyes
countshow's bodyshowyes
countshow's bodymainno

Check your understanding

Why does this fail to compile?

  • A. count is local to show, so main cannot see it (correct)
  • B. show does not return a value
  • C. label and count have different types
  • D. count is declared after it is used

Answer: A

Why: count is declared inside show, so it exists only in show's frame and only while show is running. By the time main's println runs, show has returned and its frame — including count — is gone, so the compiler reports 'cannot find symbol'.

Why B tempts people
A void method not returning a value is perfectly normal, and it is not what the compiler is objecting to here.
Why C tempts people
Their types are irrelevant; the problem is where the name is visible, not what kind of value it holds.
Why D tempts people
Within show, count is declared before use. The problem is the attempt to use it from a different method entirely.

60. Why every language has a call stack

Real world

The stack diagram is not a teaching device invented for this book. It is a picture of something real in the machine.

Discussion prompt

You have seen a stack trace in an exception message twice now — in Lesson 2b and Lesson 3b. Look back at the shape of one. What is it a list of, and how does that connect to what you drew today?

Hint: Count the lines in a stack trace and count the frames in a diagram.

Answer:

A stack trace is the stack diagram, printed. Each at ... line is one frame — a method that had started and not finished when the exception was thrown, listed from the deepest outward.

That is why the last line is your main and the first is wherever it actually broke: you are reading the frames from the top of the stack down to the bottom. The advice from Lesson 3b — read the first line for what, the last for where — is advice about reading a stack diagram.

Every language with methods has one, under one name or another, and every debugger shows it. Learning to draw it by hand is what makes the debugger's version readable.

61. How sure are you?

Commit first

Commit to an answer and to your confidence.

Predict first

A method's parameter is named hour, and its caller also has a variable named hour. How many variables named hour exist while the method is running?

  • Two — one in each frame
  • One — they are the same variable
  • Two, but they always hold the same value
  • It depends on the types

Correct: Two — one in each frame

Why: Each method gets its own frame, and each frame holds its own parameters and local variables. Two variables sharing a name in different methods are entirely separate memory locations that can hold different values — in the book's example, main's hour is 11 while printTime's is 12. The shared name is a coincidence of your choosing, and Java attaches no meaning to it whatsoever.

62. Explain it to someone else

Explain it

Two minutes, drawing as you go.

Discussion prompt

Explain to someone who has only written single-method programs what happens, step by step, when one method calls another. Use the words frame, argument, parameter and return, and draw the stack at two different moments.

Hint: The two moments worth drawing are just after the call and just after the return.

Answer:

When main reaches a call, it first works out the value of each argument. Then a new frame appears for the called method, holding its parameters — each one assigned a copy of the matching argument. main pauses where it is. The new method runs using only what is in its own frame. When it returns, its frame is thrown away and main carries on at the statement right after the call.

The two drawings should differ in exactly one way: the second has one fewer frame. If your listener asks 'but where did the method's variables go?', the answer — they no longer exist — is the whole reason the next lesson is about return values.

63. Exit ticket

Exit ticket

One question before you close the deck.

Predict first

Where does execution go when a method finishes?

  • Back to the statement immediately after the call that invoked it
  • To the next method defined in the file
  • Back to the first statement of main
  • To the end of the program

Correct: Back to the statement immediately after the call that invoked it

Why: A method invocation is a detour: you jump to the invoked method, run its statements, and then come back and pick up exactly where you left off. Not at the start of the calling method, and not at the next definition in the file — at the very next statement after the call. That is what makes the blank lines in NewLine appear between the two lines of text rather than before or after both.

64. Draw the whole lesson

Connect it up

One page, from memory.

Draw it

Draw the stack diagram for the NewLine program at three moments: just after main starts, while newLine is running, and just after threeLine returns. Label each frame with its method name and put any variables inside. Then, beside the diagrams, write the three events of a method call in order — evaluate the arguments, pass the parameters, run and return — and one sentence saying why a method cannot change its caller's variables.

65. Recap

Recap

Five sections that turn a program from a single list of statements into a set of named, independent, testable pieces.

if you remember one thingit is this
about executionthe file lists definitions; main decides the order
about parameterspassing is copying
about scopea variable lives in one frame and dies with it

Sources

  1. Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 4 (Methods and Testing), Sections 4.1-4.5, pp. 51-58
  2. The Java Tutorials — Defining Methods
  3. Think Java 2e — free online edition and source code

Want this taught 1-on-1? Alexander tutors Java — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108