Using Static Subroutines in Programs
The GuessingGame program places the logic for one game in the static subroutine playGame(), and main() calls it inside a do-while loop so the user can play again.
Why One Game Gets Its Own Block
This article studies a complete Java program called GuessingGame. The computer picks a secret number between 1 and 100, and the user tries to guess it. After each game, the program asks whether the user wants to play again. Although the program is short, it brings together several ideas from this chapter: a static subroutine that holds the logic for one game, a main() method that calls that subroutine, a random number, a loop that keeps asking for input, and a conditional test that compares the guess with the secret number.
The central idea of the program is that the logic for one game lives in a single named block, playGame(), and main() decides when to run it. Before reading on, pause and ask yourself: if a program needs the same job done in several places, would you rather keep the instructions in one place or in every place they are needed? The main idea of subroutines is an answer to that question.
What do you think happens?
If a program contains a method named playGame() but nothing in the program ever writes playGame() as a call, what happens when the program runs?
Reveal answer
playGame() does not run at all.
A subroutine runs when the program calls it. Defining a subroutine only gives it a name and a body. Without a call statement that reaches it, its instructions are never executed.
Tracing the Order of main and playGame
The GuessingGame class contains two static methods. The main() method prints an introduction, then runs a do-while loop. Inside that loop, a single statement calls playGame(), which plays one game. The source comments this line as calling the subroutine to play one game. When playGame() finishes and returns, control goes back to main(), which asks the user whether to play again and reads the answer with TextIO.getlnBoolean(). If the user chooses to play again, the do-while loop repeats and playGame() is called once more.
Trace the order by reading the flowchart from top to bottom. main() runs first and prints the introduction. The do-while loop then calls playGame(), and the rest of main() waits until playGame() returns. Only after the game ends does main() ask the play-again question. A yes answer sends control back to the top of the do-while loop, which calls playGame() again with a brand-new game. A no answer lets main() finish.
Anatomy of the playGame Header
A subroutine is a block of code that can be called multiple times from different parts of a program. In this header, the name is playGame() and the body is the code between its braces.
- static means the method belongs to the class and can be called directly from
main(), which is also static. - void means the subroutine does not return a value to its caller.
- The parentheses are empty, so
playGame()takes no input values from the caller in this program.
Local Variables Inside playGame
The first lines of playGame() declare three variables. Each one has a comment in the source that explains its role. computersNumber holds the random number picked by the computer. usersGuess holds a number entered by the user as a guess. guessCount holds the number of guesses the user has made so far.
| Variable | What it stores | When it changes |
|---|---|---|
| computersNumber | The secret number picked by the computer, an integer from 1 to 100 | Assigned once at the start of each game |
| usersGuess | The most recent guess typed by the user | Replaced each time the loop reads a new guess |
| guessCount | How many guesses the user has made so far | Reset to 0 at the start, increased by one per guess |
The three local variables of playGame()
Because these variables are declared inside playGame(), they belong to that one subroutine. They exist while the game is being played and are not visible to main(). This is why each call to playGame() can start a fresh game with its own secret number and its own guess count, without any extra bookkeeping in main().
Picking the Secret Number
The computer's number is assigned with a single statement, and the source explains that the value is a randomly chosen integer between 1 and 100, inclusive. The expression that produces it is worth reading piece by piece, because each part does one job. The expression is (int)(100 * Math.random()) + 1.
Tracing one value through the expression
Suppose Math.random() happens to return 0.537. Trace the value through (int)(100 * Math.random()) + 1.
- 1Multiply
100 * 0.537 gives 53.7.
- 2Cast to int
(int) keeps only the whole-number part, so 53.7 becomes 53.
- 3Add 1
53 + 1 gives 54, which is inside the range 1 to 100.
The secret number would be 54. The +1 is what moves the range up from 0, so the smallest possible result is 1 and the largest is 100.
The example above uses a value of 0.537 chosen for illustration, not a value the program actually produces. The same steps apply to any value Math.random() returns, which is what allows the expression to cover the whole range of 1 to 100.
Reading and Counting Guesses in the Loop
After setting up the game, playGame() sets guessCount to 0, prints a prompt, and enters a while (true) loop. The source explains that this loop repeatedly asks the user for input. Inside the loop, TextIO.getInt() reads the user's guess and stores it in usersGuess, and guessCount++ adds one to the count of guesses. The comparison between the guess and the secret number follows in the same loop body.
Calls That Start Fresh Each Time
Because playGame() is a named block, main() can call it at several points during one run. Each call starts with its own local variables. When the first call finishes, control returns to the line in main() that made the call, and the second call later begins from the top of playGame() with a new secret number and a guess count of 0. The following small program is a generated illustration of the same pattern, with one static subroutine called at two points.
public class Greeting {
static void sayHello() {
System.out.println("Hello!");
}
public static void main(String[] args) {
sayHello();
sayHello();
}
}- 1The class Greeting contains two static methods, just as GuessingGame contains
main()andplayGame(). - 2
sayHello()holds one printed line. Defining it does not print anything. - 3The first call inside
main()runs the body and prints Hello!. - 4The second call runs the same body again and prints Hello! a second time.
sayHello()is defined once, as a static method.main()callssayHello()at two different points.- The body of
sayHello()runs each time it is called.
Hello!
Hello!Mistakes When Defining and Calling Subroutines
Common mistake Expecting a defined subroutine to run by itself
What it looks likeWriting a complete static void playGame() method but never writing playGame() inside main().
Why it failsA subroutine runs when the program calls it. Its definition is only a named block of instructions waiting to be used.
Do this insteadPlace a call statement such as playGame() at each point where the game should be played.
Common mistake Assuming a subroutine's local variables are visible to main()
What it looks likeTrying to print guessCount from main() after playGame() returns.
Why it failsThe variables are declared inside
playGame(), so they belong to that subroutine and are not visible tomain().Do this insteadKeep the work that needs those variables inside playGame(), or pass the result back through a mechanism designed for that purpose.
Common mistake Confusing the guessing range with the raw Math.random() output
What it looks likeAssuming (int)(100 * Math.random()) alone gives a value from 1 to 100.
Why it failsWithout the +1, the result starts at 0, so the range is shifted down by one.
Do this insteadAdd 1 to the cast result to get a value from 1 to 100 inclusive.
Common mistake Forgetting that each call starts a fresh game, so a secret number from one game is not reused in the next
Where ThreeN2 Uses Subroutines
The source points to a second program, ThreeN2.java, introduced in Section 4.4 as an improved version of the 3N+1 program. It is described as a program that uses subroutines and prints its output in neat columns. The source gives two reasons for this design. The first is code organization: separating work into named blocks makes the program easier to read and manage. The second is output formatting: the subroutines are part of how the results are printed in neat columns.
The source does not list the individual subroutines inside ThreeN2.java or show their code, so this article does not describe them in detail. When you open the file yourself, look for the places where the program calls a named block of code, and compare them with the places where it prints results.
Practice Tracing Calls
A program defines static void showBanner() that prints a line of stars. main() calls showBanner() once, then prints the word Done, then calls showBanner() again. Write the output in order, and state which statement in main() causes each banner to print.
Hints
- Defining
showBanner()prints nothing by itself. - Each call to
showBanner()runs the body once, and control then returns tomain().
Using the expression (int)(100 * Math.random()) + 1, state the smallest and largest values it can produce, and explain why the +1 is needed.
Hints
- Consider the range that
Math.random()covers before the multiplication. - Think about what (int) does to a decimal number.
Key Takeaways from the Trace
- 1A subroutine is a named block of code that runs only when a program calls it, and it can be called many times from different points.
- 2In GuessingGame,
main()prints an introduction and then callsplayGame()inside a do-while loop so the user can play again. - 3The local variables
computersNumber,usersGuess, andguessCountbelong toplayGame(), so each call starts a fresh game with a new secret number and a guess count of 0. - 4The expression (int)(100 *
Math.random()) + 1 produces an integer from 1 to 100 inclusive, and the +1 moves the range up from 0.
At a glance
- A subroutine is a named block of code that runs only when a program calls it, so it can be reused at many points.
- The GuessingGame program places the logic for one game in
playGame(), andmain()calls it inside a do-while loop. - Local variables declared inside
playGame()start fresh on each call, giving every game its own secret number and guess count. - The expression (int)(100 *
Math.random()) + 1 produces a random integer from 1 to 100 inclusive.