Static Subroutines and Variables in Java
Modifiers appear at the front of a subroutine definition and set characteristics such as whether it is static or public.
Why Programmers Read the Header First
A subroutine is a block of code that can be called multiple times from different parts of a program. Before you use one, you need to know what it promises and what it needs. Experienced programmers rarely read the body of a subroutine first. They read the header, the line that comes before the braces and the code inside them. The header describes how the subroutine can be used: what it is, what it hands back, and what it needs to be given. You can think of the header as a short contract between the subroutine and anyone who calls it.
What do you think happens?
If a subroutine's header uses the word void in the return type position, what does it hand back to the code that calls it?
Reveal answer
No value. The word void means the return value is empty or non-existent, so callers should not expect any result from it.
The source explains that void marks a subroutine that is not a function. Such a subroutine does its work, but it does not give a value back to the caller.
Reading a Header from Left to Right
Read a header from left to right and you meet three kinds of information in order. First come the modifiers at the front, words such as static or public that set characteristics of the subroutine. Next comes the return type, which describes what kind of value the subroutine gives back, or the word void when it gives back nothing. After the return type comes the subroutine's name. The parameter list sits last, inside the parentheses that follow the name. For each piece, ask the same question: what does this part promise or require when someone uses the subroutine?
The diagram labels each piece of one header. Reading it once, you learn that the subroutine is public and static, that it hands nothing back because of void, that it is called printTable, and that it needs one int value, which it will refer to as n inside its body. You learned all of that without reading a single line of the body.
What Modifiers Set
Modifiers are the words that can appear at the start of a subroutine definition, before the return type. Their job is to set characteristics of the subroutine. The source names static and public as the two modifiers it has encountered so far, and it highlights whether a subroutine is static as one such characteristic. Modifiers change how a subroutine behaves or is accessed. They do not change what the subroutine computes.
A helpful way to hold this idea is to treat each modifier as an answer to a yes-or-no or which-kind question. One question is whether the subroutine belongs to the class as a whole rather than to one object. Another is whether it is open for outside code to call. Notice what modifiers do not answer. They do not describe the values the subroutine accepts or returns. Those jobs belong to the return type and the parameter list.
Return Type: Function or void
The return type sits between the modifiers and the subroutine's name, and it depends on what kind of subroutine you are writing. A function is a subroutine whose job is to compute some value. For a function, the return type names the type of value that comes back to the caller. That type can be a type name such as String or int, or an array type such as double[]. If the subroutine is not a function, it does not hand a value back. In that case the return type position holds the special word void, which indicates that the return value is empty or non-existent.
| Feature | Function | Subroutine with void |
|---|---|---|
| Job | Computes some value | Does work without handing a value back |
| Return type in header | A type name such as int, String, or double[] | The word void |
| What the caller can expect | A value of the declared type | No result |
| Example header | public static int square(int n) | public static void printTable(int n) |
Comparing a function with a subroutine whose return type is void
The word void carries a promise of its own. When a header says void, callers should not expect any result from that subroutine. When a header names a type, callers should expect a value of that type to come back. Reading the return type therefore tells you immediately whether the subroutine can be used inside an expression that needs a value.
Parameter Lists as Inputs
The parameter list sits inside the parentheses that follow the subroutine's name. Parameters carry information from outside the subroutine into it, so that its internal computations have something to work with. The source calls parameters part of the interface of a subroutine, meaning they are the part callers must match when they use it. A parameter declaration has the form type followed by parameter name, and the parameter list can be empty.
Consider a television with a method that changes the channel. The method needs to know which channel to switch to, so it needs an input. If a channel number is an integer, the parameter's type is int. The parameter is the spot in the header where the subroutine says what kind of input it expects and gives that input a name it can use inside its body.
Tracing Repeated Calls from Main
A subroutine runs when the program calls it, not merely because it exists in the file. The important word is called. Because the same block can be called more than once, one piece of code can serve many moments in a single run. In the Printer program above, main calls printTable three times. Each call transfers control into printTable, the body runs with n set to the value passed in, and then control returns to main so that main can continue with its next line.
Read the diagram from top to bottom to trace one run. Main starts, then reaches its first call, and control moves into printTable with n equal to 3. When the body finishes, control returns to main at the line just after that call. The same pattern repeats for the second and third calls. The body of printTable was written once, but it ran three times, each time with a different value for n.
Worked Example: Reading a Two-Parameter Header
What does this header promise?
Read the header public static double average(double x, double y) and state what a caller must supply and what the caller receives.
- 1Modifiers
public and static appear before the return type. They set access and class-level characteristics, not the values involved.
- 2Return type
The word double sits before the name. Because it is a type name rather than void, this subroutine is a function, and a double value comes back to the caller.
- 3Name
The name is average. Callers use it to make the call.
- 4Parameter list
Two parameters appear inside the parentheses, separated by a comma. Each has its own type: double x and double y.
- 5Caller's obligation
A caller must supply two double values, in order, such as
average(3.0, 5.0).
Callers must pass two double values and should expect one double value back. The body of average is not needed to know this.
Mistakes When Writing Headers
Common mistake Listing several parameters with one shared type
What it looks likepublic static double average(double x, y)Why it failsEach parameter needs its own type. The declaration gives no type for y.
Do this insteadpublic static double average(double x, double y)Common mistake Expecting a value from a void subroutine
What it looks likedouble result = printTable(3);Why it failsThe void return type promises that no value comes back, so there is nothing to store.
Do this insteadCall printTable(3) on its own line, and use a function with a non-void return type when a value is needed.
Common mistake Using a modifier to describe values
Why it failsModifiers set characteristics such as static or public. The values a subroutine accepts and returns come from the parameter list and the return type.
Common mistake Reading the body before the header
Why it failsThe header states how the subroutine can be used, so reading it first tells you what to expect from the body and what to supply when you call it.
Subroutines in ThreeN2.java
The source points to ThreeN2.java as a concrete program that uses subroutines. It is introduced in Section 4.4 as an improved version of the 3N+1 program, and the source describes it 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.
Practice Reading Headers
Here is a header: public static void showPair(String label, int value). Without reading any body, state (a) what the subroutine hands back to its caller, (b) how many inputs a caller must supply and of what types, and (c) what the name of the first parameter is. Then write a main method that calls showPair twice with different arguments, and predict how many lines the program prints before you run it.
Hints
- Look at the word before the name to answer part (a).
- Count the declarations inside the parentheses, and read each one as type followed by name.
- Each call to
showPairruns its body once, so two calls mean two runs of the body.
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 calls it.
- Read a header left to right: modifiers such as static and public set characteristics, the return type says what comes back, and the parameter list says what must be supplied.
- A function's return type names the type of value returned, while void means the subroutine hands no value back to its caller.
- Each parameter in a list needs its own type, written as type followed by name, such as double x, double y.
ThreeN2.javauses subroutines to organize its code and to format its output in neat columns.