Understanding Strings, Classes, and Subroutines
A subroutine is a named set of program instructions designed to perform a task.
The Same Job in Three Places
Imagine a program that needs the same job done in three different places. For example, it might need to print the same kind of divider line between three groups of results. You could copy the same lines of instructions into all three spots, or you could write those lines once and let the rest of the program use them whenever they are needed. The second option is the idea behind a subroutine.
What do you think happens?
If a program needs the same printed output in several places, where would you rather keep the instructions for it?
Reveal answer
Written once in one place and referred to wherever it is needed
This is the main idea of a subroutine. The instructions are grouped together, given a name, and kept in one place, so the rest of the program can refer to that name whenever the task is needed.
What a Subroutine Is
A subroutine is a named set of program instructions designed to perform a task. Grouping the instructions and giving the group a name lets a program refer to that task as one unit. Instead of treating the instructions as unrelated individual steps, the program can refer to the named subroutine when that task is needed.
The source describes a subroutine as a block of code that can be called multiple times from different parts of a program. The key word is called. A subroutine does not run simply 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.
What a Call Statement Does
A subroutine does not perform its task merely because its instructions have been grouped and named. The program must use a subroutine call statement. Writing the subroutine defines the named task; the call statement is the program's request that the task be performed right now. When the program reaches the call, control moves to the subroutine's instructions, and the task runs. Once the subroutine finishes, the program resumes at the statement that follows the call.
Read the diagram from top to bottom as the path one run of the program takes. The subroutine body is written in only one place, but execution can pass through it at a point chosen by the call. The diagram shows one call. Each further call would send execution through the same instructions again, which is what the next section traces.
Tracing Repeated Calls in One Run
The following small program is an original illustration written for this article, not an example from the source. It defines a named subroutine called printDivider that prints a line of dashes, and then the main program calls that subroutine at three points. The keywords shown are Java syntax used only to make the idea concrete; the source itself does not teach this syntax in detail.
public class Report {
public static void printDivider() {
System.out.println("----------");
}
public static void main(String[] args) {
System.out.println("Start");
printDivider();
System.out.println("Middle");
printDivider();
System.out.println("End");
printDivider();
}
}- 1The definition of
printDivideris written once and is not run just because it exists. - 2main prints Start first, since that statement comes first in the program.
- 3The first
printDivider(); call requests the task, so the dash line prints. - 4Control then returns to main, which prints Middle.
- 5The second call requests the same task again, and the same dash line prints.
- 6main prints End, then the third call prints the dash line a final time.
printDivideris defined once, as a named group of instructions- The call
printDivider(); appears three times in main - Each call sends the run into the same
printDividerinstructions
Start
----------
Middle
----------
End
----------Counting executions in one run
In the Report program, how many times does the printDivider instructions execute during one run of main?
- 1Find the call statements
main contains three statements of the form
printDivider();, one after Start, one after Middle, and one after End. - 2Trace the order
Start prints first, then the first call runs, then Middle prints, then the second call runs, then End prints, then the third call runs.
- 3Count the executions
Each call sends execution through the same instructions once, so the subroutine body executes three times, even though it is written only once.
The printDivider instructions execute three times in one run, and the program contains only one copy of them.
What do you think happens?
If main called printDivider(); five times instead of three, how many lines of dashes would the program print?
Reveal answer
Five
Each call statement requests the task once, so five calls produce five dash lines. The subroutine's instructions are still written only once.
Subroutines as Black Boxes
A subroutine can be treated as a black box. To use it, a programmer needs to know which subroutine to call and what task it provides. The programmer does not need to know the internal instructions or how those instructions were written. The call uses the subroutine as an available service while leaving its internal workings hidden. This is why the caller of printDivider needs only its name and purpose, not the details of how the dashes are printed.
Predefined Subroutines in Java and String Objects
You do not always need to write every subroutine yourself. Java includes standard classes that contain predefined subroutines. These subroutines have already been written and can be used in a program, so a program can accomplish many tasks by calling existing subroutines rather than creating every group of instructions from the beginning. The source also identifies String values as objects that contain subroutines for manipulating those strings. When you work with a String, you can use subroutines associated with that object instead of writing all the string-manipulation instructions yourself.
HELLO- word is a String object
toUpperCase()is a predefined subroutine associated with the String object- The programmer calls it without writing the letter-conversion instructions
Where ThreeN2.java Uses Subroutines
The source points to a concrete program that uses subroutines: ThreeN2.java, introduced in Section 4.4 as an improved version of the 3N+1 program. The source describes it as a program that uses subroutines and prints its output in neat columns. The source gives two reasons it relies on subroutines. 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 the subroutines are part of how that formatting is achieved.
Mistakes Beginners Make with Subroutines
Common mistake Assuming a subroutine runs just because it is written in the program
What it looks likeWriting printDivider's instructions and expecting dashes to print, without any printDivider(); call in main.
Why it failsThe source is explicit that a subroutine does not perform its task merely because its instructions have been grouped and named. It runs only when the program reaches a call statement.
Do this insteadMake sure a call statement exists at each point where the task is needed.
Common mistake Copying the same instructions into several places instead of calling one named subroutine
What it looks likePasting the dash-printing lines three times into main.
Why it failsThis repeats the instructions and makes later changes harder, since every copy must be found and updated.
Do this insteadWrite the instructions once as a named subroutine and call it wherever the task is needed.
Common mistake Thinking you must know the internal instructions before you can call a subroutine
Why it failsA subroutine can be used as a black box. A caller needs to know which subroutine to call and what task it provides, not how it works inside.
Do this insteadRead the subroutine's name and its stated task, then call it.
Practice Tracing Repeated Calls
Using the Report program from the earlier example, suppose main is changed so that it prints Start, calls printDivider(); twice in a row, and then prints End. Write out the lines the program prints, in order, and state how many times the printDivider instructions execute.
Hints
- Trace the statements of main from top to bottom, one at a time.
- Each
printDivider(); statement sends execution into the subroutine once, then returns to main. - Count the call statements to find the number of executions.
Explain, in your own words, why the programmer who calls printDivider does not need to know how the dashes are printed. Then name one reason a larger program benefits from organizing its repeated tasks into named subroutines.
Hints
- Think about the black box idea: what does the caller need to know?
- Consider the two reasons the source gives for
ThreeN2.java: code organization and output formatting.
Key Takeaways
- A subroutine is a named set of program instructions designed to perform a task, and it can be called multiple times from different parts of a program.
- A subroutine runs only when the program reaches a subroutine call statement, which requests that the named task be performed.
- Each call sends execution into the same written instructions, so one copy of the code can serve many points in a single run.
- A subroutine can be used as a black box: the caller needs its name and task, not its internal instructions.
- Java standard classes and String objects provide predefined subroutines, and
ThreeN2.javauses subroutines for code organization and neat output formatting.