Declaring Local and Instance Variables
OCP Exam Topic — Understand scoping, initialization rules, default values, effectively final variables, and the use of
varin method bodies. Covered in Chapter 5 of OCP Java SE 21 Developer Study Guide (Selikoff & Boyarsky).
Local Variables
A local variable is declared inside a method, constructor, or initializer block. It lives only while that block is executing and is destroyed when the block exits.
The critical rule: local variables must be explicitly initialized before they are read. The compiler tracks every possible execution path and reports an error if a variable might be read before it has been assigned.
javavoid example() {
int x; // declared but not initialized
System.out.println(x); // compile error: variable x might not have been initialized
}
void fixed() {
int x = 0; // initialized
System.out.println(x); // OK
}The compiler analyzes branches, not just linear flow:
javavoid branch(boolean flag) {
int x;
if (flag) x = 10;
// compile error: x is only initialized on one branch
System.out.println(x);
}
void branchFixed(boolean flag) {
int x;
if (flag) x = 10;
else x = 20; // both branches initialize x
System.out.println(x); // OK
}Instance Variables
An instance variable (also called a field) is declared inside a class but outside any method. It exists for the lifetime of the object that contains it.
Instance variables are automatically assigned a default value if not explicitly initialized:
| Type | Default Value |
|---|---|
byte, short, int, long |
0 |
float, double |
0.0 |
boolean |
false |
char |
'' (null character) |
| Object reference (any class) | null |
javaclass Counter {
int count; // defaults to 0
String label; // defaults to null
boolean active; // defaults to false
void show() {
// No compile error — instance variables always have a value
System.out.println(count + " " + label + " " + active);
}
}Because default values are guaranteed, the compiler does not require instance variables to be explicitly initialized before use.
Variable Scope
Scope defines where a variable is accessible. Java uses block scope: a variable is accessible from the point of declaration to the closing brace of the block in which it was declared.
javavoid scopeDemo() {
int outer = 1;
{
int inner = 2;
System.out.println(outer); // OK
System.out.println(inner); // OK
}
System.out.println(outer); // OK
System.out.println(inner); // compile error: inner is out of scope
}Loop variables are scoped to the loop body:
javafor (int i = 0; i < 5; i++) {
System.out.println(i); // OK inside loop
}
// System.out.println(i); // compile error: i out of scopeEffectively Final Variables
A variable is effectively final if it is never reassigned after its initial assignment — even if it was not declared with the final keyword. The compiler treats it the same as a final variable.
This matters in particular for lambda expressions and local classes, both of which can only capture local variables that are effectively final:
javavoid process(List<String> names) {
String prefix = "Hello, "; // effectively final — never reassigned
names.forEach(name -> System.out.println(prefix + name)); // OK
String greeting = "Hi";
greeting = "Hey"; // reassigned — no longer effectively final
// names.forEach(name -> System.out.println(greeting + name)); // compile error
}Declaring a variable final explicitly is still good practice because it makes the intent clear and catches accidental reassignments at compile time.
Using var in Method Bodies
Java 10 introduced local variable type inference with var. When you use var, the compiler infers the type from the initializer expression.
var is only permitted in:
- Local variable declarations with an initializer
forloop index variables- Enhanced
forloop variables try-with-resources variables
var is not permitted for:
- Instance or class (static) fields
- Method parameters
- Return types
varwithout an initializer
javavoid varExamples() {
var count = 42; // inferred as int
var name = "Alice"; // inferred as String
var list = new ArrayList<String>(); // inferred as ArrayList<String>
for (var item : list) { // inferred as String
System.out.println(item);
}
}
// Compile errors involving var:
// var field = 10; // NOT allowed as instance variable
// void method(var x) { } // NOT allowed as parameter
// var noInit; // NOT allowed without initializerOnce inferred, the type is fixed — var does not make Java dynamically typed:
javavar x = 5;
x = "hello"; // compile error: int cannot receive a StringKey Points to Remember
- Local variables have no default value and must be initialized before use; the compiler enforces this on every code path.
- Instance variables always have a default value (
0,false,null, etc.) and do not require explicit initialization. - Scope is determined by the enclosing block; variables are not accessible outside it.
- A variable is effectively final if it is assigned exactly once; lambdas and local classes can only capture effectively-final local variables.
varinfers the type from the initializer and is legal only for local variables,forloop variables, andtry-with-resources; it is not allowed for fields, parameters, or return types.