Scope, shadowing and final

Core Java · lesson 37 of 42 · 3 min read

Know where a name is visible, when it dies, and what effectively final really means.

Open this lesson in the learning hub

Key points

  • A local is visible from its declaration to the closing brace of its block, and not one line further.
  • A loop variable declared in the header (for (int i ...)) belongs to the loop and is gone after it.
  • A field lives as long as its object; a static field lives as long as the class stays loaded.
  • A local with the same name as a field shadows it. Reach the field again with this.name.
  • final means the variable is never reassigned. It says nothing about the object it points at.
  • A lambda may only capture a variable that is effectively final — assigned once and never reassigned.

Example

public class Main {

    static int shared = 100;                 // a field: alive as long as the class is loaded

    public static void main(String[] args) {
        int total = 0;                       // method scope

        for (int i = 1; i <= 3; i++) {       // i belongs to the loop header
            int square = i * i;              // a fresh square on every pass
            total += square;
        }
        System.out.println("total = " + total + "  (i and square are already gone)");

        int shared = 7;                      // shadows the field of the same name
        System.out.println("local shadows field: " + shared + ", the field is still " + Main.shared);

        final int limit = 5;
        System.out.println("final never changes: " + limit);

        int counter = 41;                    // effectively final: assigned once, never reassigned
        Runnable r = () -> System.out.println("the lambda captured counter = " + (counter + 1));
        r.run();

        String label = "start";
        label = "reassigned";                // no longer effectively final
        System.out.println("label = " + label + "; a lambda could not capture it now");

        StringBuilder sb = new StringBuilder("mutable");
        final StringBuilder locked = sb;
        locked.append(" contents");
        System.out.println("final locks the variable, not the object: " + locked);
    }
}

Declare a name in the smallest block that needs it, and reassign nothing you capture.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Core Java course, and every lesson in it is listed on the Core Java contents page.