Concepts / Local Variables and Scope in Java

Local Variables and Scope 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.

  • Programming
Interactive lab

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

  1. Before a function runs, Python marks every name assigned inside it as local to that function, unless it is declared global or nonlocal.
  2. Reading a name searches the local namespace, then enclosing functions, then the module's globals, then the built-ins; the first hit wins.
  3. A local name read before it has a value raises UnboundLocalError (Python does not fall back to the global).
  4. global x makes assignments go to the module namespace; nonlocal x makes them go to the nearest enclosing function.
  5. 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.

Educational simulation

Loading the simulation…

Where a Variable Is Written Decides Its Reach

So far, most of the variables you have written were declared inside a subroutine. Those variables are called local variables. A class, however, can contain more than subroutines. It 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. The same kind of variable is also sometimes called a global variable.

The difference between the two kinds of variable 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 the variable's scope, meaning which code is able to see it, and its lifetime, meaning how long it exists. This article follows one particular kind of member variable, the static member variable, and shows how its placement lets one subroutine leave a value that another subroutine can use.

Tracing a Value Across Two Subroutines

Start with a question about a program. Think about what happens when one subroutine stores a number and a different subroutine later reads it. Before reading on, try to decide: can a plain local variable carry that number from the first subroutine to the second?

What do you think happens?

If addPoints is called with 10 and then with 5, what does currentScore return afterwards?

Reveal answer
Answer:

18

The member variable score is shared by all static subroutines in the class, so each call to addPoints changes the same score. The first call adds 10 plus a bonus of 2, giving 12. The second call adds 5 plus a bonus of 1, giving 18. currentScore then reads that same stored value.

java
Output
18
Notes on the code
  • score is declared outside every subroutine, so it is a member variable of Scoreboard
  • bonus is declared inside addPoints, so it is a local variable that exists only during that call
  • currentScore reads the same score that addPoints wrote
score18addPointsassigns scorecurrentScorereads scoreassignsreads
When addPoints changes score, where does the new value live, and why does currentScore see it?

The value does not live inside addPoints or inside currentScore. It lives in the memory that belongs to the class as a whole. Memory for a static member variable is allocated when the class is first loaded by the Java interpreter, and that memory remains in use as long as the class exists. Every assignment to score changes the contents of that same memory, no matter which subroutine contains the assignment. Every use of score in an expression fetches its value from that same memory, no matter where the expression is. That is why the value written by addPoints is still there when currentScore runs.

Why Local Variables Disappear

A local variable is declared inside a subroutine, so each subroutine gets its own private variable. When the subroutine finishes, the local variable's value disappears. Here, bonus is a local variable of addPoints. Each call to addPoints creates a new bonus, computes it, uses it, and then lets it go. currentScore has no way to reach bonus, because bonus is not inside currentScore and is not a member of the class.

Scoreboardclassscorestatic member variablebonuslocal variableaddPointsstatic subroutinecurrentScorestatic subroutinecontainscontainscontainsdeclares
Which names can a subroutine reach? The class holds its member variable and subroutines side by side, while bonus is nested inside addPoints alone.

The two edges from the subroutines to score are the important part. Each subroutine has a link to the same variable, which is the structural version of the rule that static member variables are shared by all static subroutines in the class. The class is the container, and score sits directly inside it, so every static subroutine in that class can reach it.

Anatomy of a Static Member Declaration

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.

java
privateoptional access controlstaticbelongs to the class,shared by its staticsubroutinesinttype of the variablescorename of the variable= 0optional initial value
What does each token in this declaration contribute, and which part controls sharing, type, and initial value?
  1. 1
    Read the modifier list at the front. static means the variable belongs to the class as a whole, not to one subroutine. private or public, if present, controls who may access it.
  2. 2
    Read the type next. int tells Java the kind of value the variable holds, the same way it does for a local variable.
  3. 3
    Read the name. score is the identifier that every static subroutine in the class uses to reach the stored value.
  4. 4
    Read the optional initializer. = 0 assigns a starting value. If it is omitted, Java still gives the variable a default, covered later in this article.

Several variables of the same type can share one line. The source notes that one line can declare several variables of the same type, with the modifiers applied to the whole declaration. Every line in the example has no subroutine around it, which is what makes these member variables rather than locals.

Default Values for Unassigned Members

Local variables and member variables handle a missing assignment differently. When you declare a local variable in a subroutine, you have to assign it a value before you can do anything with it. Member variables do not have that requirement. They are automatically initialized with a default value, so a member variable that is never assigned still holds a well-defined value.

Kind of variableValue if never assignedPractical consequence
Member variable of a numeric typezeroReads return 0 before any assignment
Member variable of type booleanfalseReads return false before any assignment
Member variable of type charthe Unicode zero characterReads return that character before any assignment
Member variable holding an object such as StringnullUsing it before assignment leads to a null value, not an empty string
Local variable of any typeno defaultMust be assigned before it is used, or the code will not compile

Default values Java gives unassigned variables, compared with local variables

java
Output
Compile error: variable total might not have been initialized
Notes on the code
  • total is a local variable because it sits inside demo
  • Java gives no default to a local variable, so reading it before assignment is rejected

What do you think happens?

If score is declared as a static int member with no initializer, and the program calls currentScore before any addPoints call, what does it return?

Reveal answer
Answer:

0

An unassigned static int member variable receives the default value zero. Because the member variable is never assigned, the default is what currentScore reads.

Mistakes Beginners Make with Scope

  • Common mistake

    Expecting a local variable to keep its value after the subroutine returns

    What it looks like

    Declaring int bonus = points / 5 inside addPoints, then reading bonus inside currentScore.

    Why it fails

    A local variable exists only while its subroutine runs. bonus is not inside currentScore and is not a member of the class, so currentScore cannot reach it.

    Do this instead

    If the value must survive across subroutines, declare it as a static member variable outside every subroutine.

  • Common mistake

    Assuming a local variable starts at zero like a member variable

    What it looks like

    Declaring int total; inside a subroutine and printing it right away.

    Why it fails

    Member variables receive defaults, but local variables do not. The code fails to compile because total was never assigned.

    Do this instead

    Assign a value to a local variable before using it, for example int total = 0;.

  • Common mistake

    Expecting an unassigned String member to be an empty string

    What it looks like

    Declaring static String label; with no initializer and assuming label holds empty text.

    Why it fails

    An unassigned String member variable starts as null, not as an empty string.

    Do this instead

    Assign an explicit value such as an empty string if you need one, or check for null before using the value.

  • Common mistake

    Forgetting that the static modifier is what makes the value shared, so omitting it changes who can see the variable

Practice Tracing Shared State

MEDIUM

Using the Scoreboard class above, trace the program if main called addPoints(5) twice instead of addPoints(10) and addPoints(5). Work out the value of score after each call and the value printed by currentScore. Then explain which variable is the local one and which is shared.

Hints
  • For each call, compute bonus first as points divided by 5, then add points and bonus to score.
  • The value after the first call becomes the starting value for the second call, because score is a member variable.
  • bonus is discarded when each call finishes; score is not.

Summary of Scope and Lifetime

  1. 1
    A local variable is declared inside a subroutine and exists only while that subroutine runs.
  2. 2
    A member variable is declared outside any subroutine but inside a class; a static member variable belongs to the class and exists as long as the class exists.
  3. 3
    Static member variables are shared by all static subroutines in the class, so a value set in one subroutine can be read in another.
  4. 4
    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 receive no default and must be assigned before use.

At a glance

  • A local variable is declared inside a subroutine and disappears when that subroutine finishes.
  • A static member variable is declared outside every subroutine but inside a class, and it exists as long as the class exists.
  • Every static subroutine in the class reads and writes the same static member variable, which is how a value carries from one subroutine to another.
  • Unassigned member variables get defaults such as zero, false, the Unicode zero character, or null, while local variables must be assigned before use.