Where can a name be used?
Every variable has a region of the program in which its name is meaningful. Outside that region the name does not exist. That region is the variable’s scope. Scope matters because the same name can mean different things in different places, and because programs that look fine can behave wrongly when a name quietly refers to a different variable from the one you meant.
This topic has two parts: where variables can be used and which variable a name refers to when more than one has the same name.
Local variables
Local variables are variables declared in the headers or bodies of blocks of code. A block is a section of code in braces: a method body, a constructor body, an if, an else, a loop. A local variable can be accessed only in the block in which it is declared. As soon as the block ends, the variable no longer exists.
for (int i = 0; i < 3; i++)
{
int square = i * i; // i and square exist only inside the loop
}
// here, neither i nor square exists
Constructors and methods are blocks of code, so parameters to constructors or methods are also local variables. They may be used only within the constructor or method. And because they are not members of the class, local variables, parameters included, cannot be declared public or private.
Instance variables and class variables
Instance variables and class variables are declared in the class, outside any method. They are in scope throughout the class: any method or constructor of the class can use them. From outside the class, public and private decide whether they can be seen (Topic 3.3).
public class Calc
{
private int x = 2;
public int calc(int y)
{
int z = x + y;
if (z > 5)
{
int w = z * 2;
z = w;
}
return z;
}
}
With c = new Calc(), c.calc(1) gives z = 2 + 1 = 3; the if is skipped, so w never exists and the method returns 3. c.calc(4) gives z = 6; the if runs, w is 12, z is set to 12, and the method returns 12. The compiled program printed 3 12. Writing return w; at the end would not compile, because w is out of scope there.
When two variables share a name
If a local variable or parameter has the same name as an instance variable, then inside the body of the constructor or method the name refers to the local variable (or parameter). The instance variable is hidden there.
This is the source of a classic bug. Study this class:
public class Pet
{
private String name;
private int age;
public Pet(String name, int age)
{
name = name;
age = age;
}
public String describe()
{
return name + " " + age;
}
}
Pet p = new Pet("Rex", 4);
System.out.println(p.describe());
It compiles, and it prints:
null 0
Inside the constructor, name = name; means “assign the parameter name to the parameter name”. The instance variable name is hidden, so it is never set and keeps its default null; likewise age stays 0. Nothing in the language warns you. The fix is to use different names:
public class PetFixed
{
private String name;
private int age;
public PetFixed(String petName, int petAge)
{
name = petName;
age = petAge;
}
public String describe()
{
return name + " " + age;
}
}
which prints Rex 4. (Topic 3.9 shows a second fix, using this, which lets you keep the same name and still reach the instance variable.)
Worked example 1: a parameter and a local variable that hide an instance variable
Task. Trace this class.
public class Scorer
{
private int total = 0;
public int bonus(int total)
{
total = total + 5;
return total;
}
public int localOnly()
{
int total = 100;
total++;
return total;
}
public void add(int points)
{
total += points;
}
public int getTotal()
{
return total;
}
}
Scorer s = new Scorer();
s.add(7);
System.out.println(s.bonus(20));
System.out.println(s.getTotal());
System.out.println(s.localOnly());
System.out.println(s.getTotal());
Step 1: initial state. The instance variable total is 0.
Step 2: s.add(7). The parameter is points, so total += points; refers to the instance variable. total is 7.
Step 3: s.bonus(20). Here the parameter is called total, so it hides the instance variable. The parameter starts at 20; total = total + 5 makes the parameter 25; the method returns 25. The instance variable is untouched.
Step 4: s.getTotal(). It has no local total, so it returns the instance variable: 7.
Step 5: s.localOnly(). It declares a new local variable int total = 100; which hides the instance variable. total++ makes the local 101, which is returned. The instance variable is untouched.
Step 6: s.getTotal(). Still 7.
| Statement | Which total is used? |
Local / parameter total |
Instance total |
|---|---|---|---|
add(7) |
instance (no hiding) | none | 7 |
bonus(20) |
parameter | 25 | 7 |
getTotal() |
instance | none | 7 |
localOnly() |
local | 101 | 7 |
getTotal() |
instance | none | 7 |
The program printed 25, 7, 101, 7.
Check. If you meant bonus to change the object’s total, the parameter name is the bug. It must have a different name (and the body must then update the instance variable).
Worked example 2: finding scope errors
Task. This class does not compile. Find each error, say why, and fix it.
public class ScopeErr
{
private int sum = 0;
public int first()
{
for (int i = 0; i < 3; i++)
{
int square = i * i;
sum += square;
}
return i;
}
public int second()
{
if (sum > 0)
{
int doubled = sum * 2;
}
return doubled;
}
public int third()
{
int temp = 4;
return sum + temp;
}
public int fourth()
{
return temp;
}
}
Step 1: first. i was declared in the for header, so it exists only inside the loop. At return i; the compiler reports cannot find symbol for i. Fix: return sum, or declare int i; before the loop if the final value is needed.
Step 2: second. doubled is declared inside the if block, so return doubled; is outside its scope: cannot find symbol. Fix: declare int doubled = 0; before the if, assign inside it, and return afterwards.
Step 3: third. temp is local to third and is used inside it: correct. sum is an instance variable, available everywhere in the class.
Step 4: fourth. temp was declared in third, a different method. Local variables are not shared between methods, so cannot find symbol. Fix: declare temp in fourth or make it an instance variable if it must be shared.
The compiler listed exactly three errors (for i, doubled and temp) and none for sum, square or the first temp. A compact rule: a name is visible from its declaration to the end of the block that contains it.
Common misconceptions
- “A variable declared in a loop or an if is available after it.” It exists only inside that block.
- “A variable in one method can be used in another method of the same class.” Only instance and class variables are shared between methods; locals are not.
- “A parameter can be private.” Parameters are local variables and take no access modifier.
- “If I write
name = name;the compiler will know I mean the instance variable.” It assigns the parameter to itself. The compiler accepts it silently, which makes it a hard bug to see. - “Giving a local variable the same name as an instance variable changes the instance variable.” It creates a separate variable that hides it for the length of the block.
- “Scope is the same as private.” Scope is about which block a name belongs to. Access (
public/private) is about which classes may use a class member.
Where this leads
The this keyword in this Keyword solves the same-name problem directly. Check this topic with the practice questions, read the revision notes and tick the topic checklist. To remind yourself how constructors set instance variables, see Constructors.
Quick questions
What is a block?
A block is a section of code enclosed in braces. The body of a method or constructor is a block, and so are the bodies of an if statement, an else, and a loop.
Why does the compiler say cannot find symbol?
Usually because the variable is used outside the block in which it was declared, or because it was never declared. After a for loop ends, the loop variable declared in its header no longer exists.
What if a parameter has the same name as an instance variable?
Inside that constructor or method, the name means the parameter, which hides the instance variable. Assigning to it changes only the local copy. Use different names, or the this keyword from Topic 3.9, to reach the instance variable.
Can a local variable be private?
No. Local variables, including parameters, cannot be declared public or private. Access modifiers apply only to members of a class: its instance variables, class variables, methods and constructors.