Static Subroutines in Java
A member variable is declared outside any subroutine but inside a class; a static member variable belongs to the class as a whole and exists as long as the class exists.
Try it: Scope: Local, Enclosing, Global, Built-in
How Python looks up a name Local → Enclosing → Global → Built-in, why assigning to a name inside a function makes it local (and raises UnboundLocalError if it is read first), and what global, nonlocal and closures change.
How it works
- Before a function runs, Python marks every name assigned inside it as local to that function, unless it is declared global or nonlocal.
- Reading a name searches the local namespace, then enclosing functions, then the module's globals, then the built-ins; the first hit wins.
- A local name read before it has a value raises UnboundLocalError (Python does not fall back to the global).
- global x makes assignments go to the module namespace; nonlocal x makes them go to the nearest enclosing function.
- A function's local frame disappears when it returns, unless an inner function (a closure) still uses its variables.
Default run (10 steps): Ready: Local shadows global. Every name is looked up Local, then Enclosing, then Global, then Built-in. … Finished. Printed: inside: 99 / outside: 10.
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Built-ins shows only the names the script used.
Loading the simulation…
Why a Value Must Outlive One Subroutine
Suppose one subroutine stores a number and a different subroutine needs to read that number later. Can this be done? With the kind of variable you have probably written most often, the answer is no. A variable declared inside a subroutine is a local variable, and each subroutine gets its own private copy. When the subroutine finishes, the value disappears.
Java offers a different kind of variable for this job: a static member variable. It is declared outside every subroutine but still inside a class, and it is not owned by any one subroutine. This article follows one small program to see how a static member variable carries a value from one subroutine call to the next, why its placement determines how long it lives and who can use it, and how the same subroutine can be called many times from main.
Tracing a Score Across Two Subroutines
The following small program is an original illustration written for this article. It has a class named Scoreboard with one static member variable, score, and two static subroutines. addPoints adds to score, and showScore prints it. Notice that score is written outside both subroutines but inside the class.
3
7- static int score; is a static member variable, declared outside every subroutine
addPointsassigns to score, so its change is kept in the variable itselfshowScorereads score even though it never assigned it- main calls
addPointsandshowScoretwice each
What do you think happens?
After addPoints(3) and addPoints(4) have both run, what does the second call to showScore() print?
Reveal answer
7
addPoints(3) sets score to 3, and addPoints(4) then adds 4 to that same memory, making score 7. showScore reads the current value of score, which is 7. Because score is a static member variable shared by all static subroutines in the class, the value set in addPoints is visible to showScore.
3
7
The first line appears after the first addPoints(3) call. The second line appears after addPoints(4) adds to the value already stored in score.Member Variables Versus Local Variables
Most variables you have written so far were declared inside a subroutine. Those are local variables. A class can also contain variable declarations that sit outside every subroutine. Such a variable is called a member variable, because it is a member of the class. It is also sometimes called a global variable.
The difference comes down to placement. A local variable is declared inside a subroutine. A member variable is declared outside any subroutine, though it still must be inside a class. That placement decides two things. The scope of a variable is which code is able to see it. The lifetime of a variable is how long it exists. A local variable lives only as long as the subroutine that contains it runs. A static member variable belongs to the class as a whole, and its lifetime is tied to the class rather than to any single call of a subroutine. This is why its value can outlast one subroutine's execution.
| Feature | Local variable | Static member variable |
|---|---|---|
| Where it is declared | Inside a subroutine | Outside every subroutine, but inside a class |
| Who can use it | Only the subroutine that declares it | Every static subroutine in the class |
| How long it exists | Ends when the subroutine finishes | Exists as long as the class exists |
| Value after one subroutine ends | Disappears | Remains available to other subroutines |
| Must be assigned before use | Yes | No, because it receives a default value |
Placement determines scope and lifetime. The same kind of value can be stored in either, but the rules for sharing it differ.
Declaring a Static Member Variable
A member variable declaration looks almost exactly like a local variable declaration. There are two differences. First, the member variable is written outside any subroutine, although it still has to be inside a class. Second, the declaration can carry modifiers such as static, public, and private. In this chapter every member variable declaration includes static. The modifiers public and private may also appear and control access, but they are not required.
- static is the modifier that makes this a static member variable
- int is the type, and it comes right before the name, as it does for a local variable
- score is the name chosen for the variable
- public, when present, controls access; it is optional
- A single line can declare several variables of the same type, such as hits and misses
Read each declaration from left to right. The modifier list comes first, then the type, then the name. Every line sits outside any subroutine, which is what makes these member variables rather than locals.
Default Values for Unassigned Member Variables
Local variables and member variables handle a missing assignment differently. When you declare a local variable in a subroutine, you must assign it a value before you can use it. A member variable has no such requirement. Java automatically initializes it with a default value, so a member variable that is never assigned still holds a well-defined value.
| Type of member variable | Default value when unassigned |
|---|---|
| Numeric types such as int | Zero |
| boolean | false |
| char | The Unicode zero character |
| Objects such as String | null |
These are the same defaults Java uses when it initializes the elements of an array.
Because these defaults are fixed, you can reason about a class before any assignment has happened. A static int declared without an initializer starts as zero. A static String declared without an initializer starts as null, not as an empty string.
What a Subroutine Is and Why It Gets Called Again
Imagine a program that needs the same job done in three different places. You could copy the same lines three times, or write them once and let the rest of the program use them whenever they are needed. A subroutine is the second option. It is a block of code that can be called multiple times from different parts of a program.
The important word 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 one run of the program. When the subroutine finishes, control returns to the place that made the call.
Following main Through Repeated Calls
Return to the Scoreboard program. Its main subroutine calls addPoints and showScore twice each. The sequence below shows the order of those messages. Each arrow is one step in the run, numbered in the order it happens.
Tracing the Scoreboard program step by step
Given the Scoreboard class shown earlier, trace what happens during main and state each printed line.
- 1Step 1: main calls addPoints(3)
Control moves from main into
addPoints. The parameter points receives 3, and score becomes 0 plus 3, which is 3. Because score is a member variable, that new value stays in place afteraddPointsreturns. - 2Step 2: main calls showScore()
Control moves into
showScore, which reads score and prints 3.showScorenever assigned score, but it can read it because score belongs to the class. - 3Step 3: main calls addPoints(4) again
The same block of
addPointsruns a second time. It reads the current value of score, which is 3, adds 4, and stores 7 in the same variable. - 4Step 4: main calls showScore() again
showScoreruns again and reads 7 from score, printing 7.
The program prints 3 and then 7. The same two subroutines are reused, and the single static variable carries the running total between them.
Mistakes With Member and Local Variables
Common mistake Expecting a local variable to keep its value after its subroutine ends
What it looks likeA subroutine declares int total = 0 inside itself, adds to it, and then another subroutine tries to read total.
Why it failsA local variable is private to the subroutine that declares it, and its value disappears when that subroutine finishes.
Do this insteadIf a value must be shared across subroutines, declare it as a static member variable outside every subroutine.
Common mistake Declaring the shared value inside a subroutine and expecting other subroutines to see it
What it looks likeWriting static int score; inside addPoints, which places it inside that subroutine.
Why it failsPlacement decides scope. A declaration inside a subroutine is local, so other subroutines cannot use it.
Do this insteadMove the declaration outside every subroutine but inside the class, as in static int score; at the top of the class.
Common mistake Assuming an unassigned static String is an empty string
Why it failsThe default for an unassigned member object such as String is null, which is different from a String holding no characters.
Do this insteadAssign an explicit value, such as an empty string, when your program depends on it.
Common mistake Assuming a local variable starts at zero just because a member variable does
Why it failsLocal variables must be assigned before use. Member variables receive defaults automatically.
Do this insteadAssign every local variable a value before using it.
Subroutines in ThreeN2.java
The source material points to a concrete program that uses subroutines: ThreeN2.java. It is introduced 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. Because the source gives this as its own example, it is a good place to see the ideas of this article in a real program.
The source gives two reasons that ThreeN2.java 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 subroutines are part of how that formatting is achieved.
Practice: Predicting a Carried Value
A class declares static int total; with no initializer. Its static subroutine add(int n) runs total = total + n. Its main subroutine calls add(5), then add(2), then calls a static subroutine show() which prints total. Before tracing, predict what show prints, then trace each call step by step. Finally, state what total would print if show were called before any call to add.
Hints
- total is a static member variable, so each call to add changes the same stored value
- The starting value of an unassigned static int is zero, which is the default for numeric member variables
- Trace the value after
add(5), then afteradd(2), before deciding what show prints
Key Takeaways
- A member variable is declared outside every subroutine but inside a class, and a static member variable belongs to the class as a whole, existing as long as the class exists.
- Static member variables are shared by all static subroutines in the class, so a value set in one subroutine can be read in another, unlike a local variable that disappears when its subroutine finishes.
- A static member variable declaration uses the static modifier, the type before the name, and the variable's name, with public or private optionally added.
- Unassigned member variables receive defaults: zero for numeric types, false for boolean, the Unicode zero character for char, and null for objects such as String. Local variables must be assigned before use.
- A subroutine is a block of code that runs whenever it is called, so main can reuse the same subroutine at many points in a run.