Calling Subroutines in Java
Local variables belong to the subroutine where they are declared.
Why Programs Reuse Code
Imagine a program that needs the same job done in three different places. One option is to copy the same lines of code three times. The other option is to write those lines once and let the rest of the program use them whenever they are needed. A subroutine is the second option. This article explains how subroutines are called, how values travel into them, how variables inside them are scoped, and how a program such as ThreeN2.java uses them.
Before reading on, pause and ask yourself: if a program needs the same calculation or the same printed output in several places, where would you rather keep the instructions for it, in one place or in every place it is needed? Hold that question in mind, because the main idea of subroutines is an answer to it.
What a Subroutine Is
A subroutine is a block of code that can be called multiple times from different parts of a program.
The important word in that definition is called. A subroutine does not run just because it exists in a program. It runs when the program reaches a point where it calls it. Because the same block can be called more than once, a single piece of code can serve many moments in a single run of the program. Writing the instructions once and calling them at each needed point is the core benefit.
A subroutine runs when the program calls it. Writing it once lets the program reuse it at many points.
Tracing Several Calls in One Run
Consider a small program, written here as an original illustration, that calls one subroutine named showLine at three different points. The subroutine prints a label and then a number. The main method calls it before the interest is calculated, after the interest is calculated, and after the balance is updated. Trace the run from top to bottom: each time main reaches a call, execution moves into showLine, runs its statements, and then returns to the statement in main that follows the call.
Start: 1000
Interest: 50
Balance: 1050showLineis defined once but called three times from main- The text inside the parentheses of each call supplies the parameters
- print leaves the cursor on the same line, so println after it completes the line
Notice that the code inside showLine appears only once in the file, yet it produces three lines of output. That is the reuse the source describes: one block of instructions, many moments of use in the same run.
Parameters in Call Statements
A subroutine call is written as the subroutine's name followed by parentheses. The information placed inside those parentheses is a parameter supplied to the subroutine. In the statement System.out.println(interest), the parameter is interest. The call hands the current value of interest to the output routine, which displays it. A call does not always need a parameter. When none is required, the call uses an empty pair of parentheses.
Following Assignments in Order
Assignments change the values that later calculations use. When tracing a program, keep three stages separate: a declaration introduces a variable, an assignment gives it a value, and a later statement may use that value in another calculation. Follow the program in its written order rather than jumping to the final answer. At every assignment, ask which variable is receiving the result and whether that result replaces an earlier value.
Tracing the interest calculation
Starting with principal equal to 1000 and rate equal to 5, what value does principal hold after the update, and what value does the balance call display?
- 1Declare and assign
principal receives 1000 and rate receives 5.
- 2First calculation
interest is assigned principal * rate / 100, which is 1000 * 5 / 100, so interest holds 50.
- 3Second assignment to principal
principal = principal + interest uses the current values 1000 and 50, computes 1050, and replaces the earlier value stored in principal.
- 4Display
The final call prints the current value of principal, which is 1050.
The balance call displays 1050. The same variable held 1000 before the update and 1050 after it.
What do you think happens?
Before looking at the result, predict the final value of principal after the statement principal = principal + interest runs, given principal is 1000 and interest is 50.
Reveal answer
1050
The right-hand side uses the current values of principal and interest, computes 1050, and assigns that result back to principal. No second variable is created for the updated investment.
Local Variables and Their Scope
Local variables belong to the subroutine where they are declared. A variable declared inside a subroutine is local to that subroutine. It exists while that subroutine is running and cannot be accessed from outside it.
A useful way to read a program is to ask which subroutine owns each variable. In the examples for this topic, the variables are declared inside main(), so main() is the part of the program that can use them. The scope of a local variable is tied to the subroutine that contains its declaration. While that subroutine is running, its local variables can be used within it. Once it is no longer running, those variables are not available from outside it. This boundary matters when deciding where a value can be read or changed.
In the Interest example, principal and interest belong to main, so showLine cannot read them directly. Its own names, label and amount, belong to showLine and are supplied by the parameters in each call. Reading the program this way answers the question of where a value can be read or changed before you trace any statements.
print Versus println
The output statements in the Interest program are subroutine call statements too. System.out.print displays the text it is given without moving to a new line. System.out.println displays its text and then adds a linefeed, so the next output begins on a new line. The Interest program combines the two deliberately: print shows the label, and println shows the number after that label and ends the line.
| Call | What it displays | Where the cursor goes next |
|---|---|---|
| System.out.print("Interest: ") | Interest: | Stays on the same line |
| System.out.println(interest) | The value of interest | Moves to a new line after the value |
Comparing print and println in the Interest program
System.out.print("Balance: ");
System.out.println(1050);
System.out.println("Done");
Output:
Balance: 1050
DoneThe first two statements share one line of output because print left the cursor after the label. The third statement starts on its own line because the preceding println added a linefeed.
Mistakes Beginners Make When Tracing Calls
Common mistake Assuming a subroutine runs just because it is written in the file
Why it failsA subroutine runs when the program reaches a point where it calls it.
Do this insteadFind each call statement and trace execution into the subroutine at that moment.
Common mistake Treating the right-hand side of an assignment as a new variable
What it looks likeprincipal = principal + interestWhy it failsThe right-hand side uses current values and assigns the result back to the same variable. No second variable is created.
Do this insteadRead the statement as: compute using current values, then replace the stored value of principal.
Common mistake Trying to use a local variable from outside its subroutine
What it looks likeUsing principal inside showLine when principal was declared in main
Why it failsA local variable is accessible only inside the subroutine that declares it.
Do this insteadPass the value in as a parameter, as the amount parameter does.
Common mistake Expecting print to end the line
What it looks likeSystem.out.print("Interest: "); followed by System.out.print(50);Why it failsprint does not add a linefeed, so the next output continues on the same line.
Do this insteadUse println for the final output on a line.
How ThreeN2.java Uses Subroutines
The source describes ThreeN2.java, an improved version of the 3N+1 program introduced in Section 4.4, 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 program prints its results in neat columns, and subroutines are part of how that formatting is achieved.
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. Ask which variables belong to each subroutine and which values are passed in through parameters.
Practice Tracing a Subroutine Call
Using the Interest program above, change rate from 5 to 10 and predict the three lines of output before running it. Then trace the output line by line, identifying which call produces each line and what value interest holds at that point.
Hints
- The first call displays principal, which is still 1000 when it runs.
- interest is 1000 * 10 / 100 at the second call.
- The third call displays principal after principal = principal + interest runs again.
Key Takeaways
- A subroutine is a block of code that can be called multiple times from different parts of a program, and it runs only when the program reaches a call to it.
- A call statement names the subroutine and places any parameter inside the parentheses; a call with no parameter uses empty parentheses.
- A local variable belongs to the subroutine where it is declared, exists while that subroutine runs, and must be declared before it is used.
- Assignments run in order and can replace a variable's earlier value, so tracing them in sequence reveals how each calculation's inputs change.
- print leaves the cursor on the same line, while println adds a linefeed, which is how the Interest program places its label and value on one line.