Local Variable Declaration

Local Variable Declarations are statements that introduce new variables to the block that contains them. Local Variable Declarations can possess modifiers that apply to the variables, and optionally, an initializer for each variable declared.

Variables declared in a Local Variable Declaration are declared (and possibly initialized) in the order they're written.

Syntax

Local Variable Declarations can be expressed through:

1 modifiers type first-var-decl additional-var-decls ;

where...

modifiers is either:
  • The keyword final, or
  • an annotation applicable to local variables.
type is any type that can be used to declare a variable (i.e., that is not void).
first-var-decl is an unused variable identifier, then any number of (optionally annotated) pairs of brackets ([]) indicating additional array dimensions, then optionally followed by an equals token (=) and an expression whose type is that of the variable. Equivalently, the first-var-decl has the form:

name array-dims = init-expr

where...

name is an identifier that is used to name the variable being declared.
array-dims is any number of consecutive pairs of [] brackets, each of which can be independently annotated by an applicable annotation.
init-expr is an expression whose type is assignable to the effective type of the variable.
additional-var-decls is any number of first-var-decls, separated by comma tokens (,).

Behavior

Local variable declarations are the basic way to introduce a new variable to a local scope (called, a local variable). Such variables exist and are accessible immediately after their declaration (even within the same local variable declaration; see below), within the scope in which they were declared.

They can be used within any local scope, such as inside static and instance initializers and method bodies.

Reading from a local variable

Local variables can be read and evaluated only after they have been definitively assigned a value (see Definite Assignment). If there is a path of execution (even a single path) that would result in the variable not being initialized by the time it is read, the read is not valid. For example, in the following code, each commented print statement illegally refers to x:

int x;
// System.out.println(x); // x not yet initialized; error
if (Math.random() > .25) {
	// System.out.println(x); // x not yet initialized; error
	x = 20;
	System.out.println(x); // x has been initialized; prints 20
}
// System.out.println(x); // x not yet initialized; error

Final Declarations

final local variable declarations create a final local variable. In contrast to final fields (class members), they do not have to be given a value (if they are not ever read). Once they have been assigned a value, though, they cannot be reassigned (similar to final fields).

final local variables can only be assigned a value once. It's a compile-time error for any path of execution to assign to a final variable more than once.

Constant Declarations

The variable introduced by a local variable declaration can be constant if all of the following are true:

  1. The local variable declaration is marked final (i.e. final is present in the list of modifiers),
  2. the variable's type is either String or some primitive type,
  3. the variable has an initializer (i.e. the variable has a corresponding = init-expr), and
  4. the init-expr used to initialize the variable is a constant expression.

Notes

  1. Declared local variables can be accessed and used immediately after their actual declaration, before the end of the local variable declaration statement that they're a part of. For example:
    int x, y, z = x = y = 10; // Initializes all 3 variables to 10.
    System.out.println(x + y + z); // All variables are accessible and initialized; prints 30.
    In the above code, x and y are accessible within the initializer expression of z, and can be assigned values before the end of the statement.

    Comparatively, declared local variables cannot be accessed or used before their declaration, even within the same statement:

    int x = y, y = 10; // Compiler error: cannot find symbol 'y'
  2. In line with the first note above, init-exprs for variables declared within the same local variable declaration are executed in order, immediately after the variable they belong to is introduced, and immediately before the succeeding variable (if any) is introduced:
    int x = 0;
    int y = ++x, z = ++x, w = ++x; // Initializes x, y, and z to 1, 2, and 3, respectively.
    // x is set to 3.
    
    System.out.println("x: " + x);
    System.out.println("y: " + y + ", z: " + z + ", w: " + w);
    This code prints:
    x: 3
    y: 1, z: 2, w: 3
  3. A final local variable can be read even if not every path of execution assigns it a value, so long as every path of execution leading up to the read assigns it a value:
    final int x;
    if (Math.random() < .5) {
    	x = 10;
    	System.out.println(x);
    } else {
    	// System.out.println(x); // x cannot be accessed here.
    }
    // System.out.println(x); // x cannot be accessed here because it may not have been initialized
  4. Constant local variables can be used to form other constant expressions, which allows certain calculations and expressions that are not otherwise allowed. For example:
    final long x = 10;
    byte b = x;
    This code compiles because x is a constant variable, meaning that its evaluation when initializing b is performed at compile-time. Since b is initialized with an integral constant-expression whose value (10) is known to be able to fit within the byte type, the assignment succeeds.

    If x's local variable declaration were not declared constant, the variable x would not be a constant variable. This would cause the evaluation of x, within b's initializer, to not undergo value-checks that determine if the value of b's initializer can fit into b, making the initialization result in the standard loss-of-precision in assignment error.