You declare int i inside a for loop. Outside the loop, i doesn't exist. Scope determines where a variable is visible. Understanding scope prevents bugs and helps you write cleaner, more predictable code.

Block scope

Variables exist only within their enclosing braces.

BlockScope.java
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}
public class BlockScope {
    public static void main(String[] args) {
        System.out.println("=== Block Scope ===\n");

        int outer = ;
        System.out.println("outer = " + outer);

        {
            // Inner block - can see outer
            int inner = ;
            System.out.println("  In block: outer = " + outer);
            System.out.println("  In block: inner = " + inner);

            // Nested block
            {
                int deepest = 30;
                System.out.println("    Deepest: outer = " + outer);
                System.out.println("    Deepest: inner = " + inner);
                System.out.println("    Deepest: deepest = " + deepest);
            }
            // deepest is gone here
        }

        // inner is no longer accessible here!
        System.out.println("After block: outer = " + outer);
        // System.out.println(inner);  // Error: cannot find symbol

        System.out.println("\n=== Scope Rule ===");
        System.out.println("Variables exist only in their { } block");
        System.out.println("Inner blocks can see outer variables");
        System.out.println("Outer blocks cannot see inner variables");
    }
}

Variables declared inside {} are not visible outside those braces.

block scope Variable visibility limited to the `{}` block where it's declared.

Variable shadowing

Inner variables can hide outer variables with the same name.

Shadowing.java
public class Shadowing {
    static int x = 100;  // Class-level x

    public static void main(String[] args) {
        System.out.println("=== Variable Shadowing ===\n");

        System.out.println("Class-level x = " + x);  // 100

        int x = 50;  // Local x shadows class x
        System.out.println("Local x = " + x);  // 50

        System.out.println("Class x via class name = " + Shadowing.x);

        {
            int y = x + 10;  // Uses local x (50)
            System.out.println("Inner block uses local x: y = " + y);

            // Cannot re-declare x in inner scope in Java!
            // int x = 25;  // Error: x already defined
        }

        // Method parameter shadowing
        System.out.println("\n=== Method Parameter Shadowing ===");
        processValue(999);
        System.out.println("After method: x = " + x);  // Still 50

        System.out.println("\n=== Shadowing Summary ===");
        System.out.println("Inner variables can hide outer ones");
        System.out.println("Use class name to access shadowed static variables");
    }

    static void processValue(int x) {  // Parameter shadows local
        System.out.println("In method, parameter x = " + x);  // 999
        System.out.println("Class x = " + Shadowing.x);  // 100
    }
}

A local variable with the same name as a field "shadows" the field.

shadowing Inner variable hides outer variable of same name. Use `this.field` to access hidden field.

Loop variable scope

Loop variables are scoped to the loop block.

LoopScope.java
public class LoopScope {
    public static void main(String[] args) {
        System.out.println("=== Loop Variable Scope ===\n");

        // For loop variable
        System.out.println("For loop:");
        for (int i = 0; i < 3; i++) {
            System.out.println("  i = " + i);
        }
        // System.out.println(i);  // Error: i not accessible

        // Variable declared outside loop
        System.out.println("\nVariable outside loop:");
        int sum = 0;
        for (int j = 1; j <= 5; j++) {
            sum += j;
            System.out.println("  j=" + j + ", sum=" + sum);
        }
        System.out.println("Final sum = " + sum);  // sum still accessible

        // Each iteration creates new local
        System.out.println("\nNew variable each iteration:");
        for (int k = 0; k < 3; k++) {
            int temp = k * 10;  // Created fresh each iteration
            System.out.println("  k=" + k + ", temp=" + temp);
        }  // temp dies here, k dies here

        // While loop scope
        System.out.println("\nWhile loop:");
        int count = 0;  // Must declare outside
        while (count < 3) {
            int local = count * 2;  // New each iteration
            System.out.println("  count=" + count + ", local=" + local);
            count++;
        }  // local dies, count survives
        System.out.println("Final count = " + count);
    }
}

The i in for (int i ...) only exists inside the loop body.

Method parameter scope

Parameters are local to the method.

MethodScope.java
public class MethodScope {
    public static void main(String[] args) {
        System.out.println("=== Method Scope ===\n");

        int result = calculate(10, 5);
        System.out.println("Result: " + result);

        // Cannot access a, b, sum from here!
        // System.out.println(a);  // Error!

        System.out.println("\n=== Multiple Calls ===");
        System.out.println("calculate(20, 3) = " + calculate(20, 3));
        System.out.println("calculate(7, 8) = " + calculate(7, 8));

        // Each call has independent variables
        System.out.println("\n=== Lifetime Demo ===");
        trackLifetime();
        trackLifetime();  // Same variable names, fresh values
    }

    static int calculate(int a, int b) {
        // a and b are parameters - local to this method
        System.out.println("  In calculate: a=" + a + ", b=" + b);

        int sum = a + b;
        int product = a * b;

        System.out.println("  sum=" + sum + ", product=" + product);

        return sum;  // After return, a, b, sum, product all die
    }

    static int callCount = 0;  // Class variable - persists

    static void trackLifetime() {
        callCount++;  // Persists across calls
        int localCounter = 1;  // Dies after method ends

        System.out.println("Call #" + callCount +
                          ", localCounter=" + localCounter);
    }
}

Parameters act like local variables, existing only during method execution.

Instance vs local variables

Distinguish between object fields and method-local variables.

InstanceVsLocal.java
public class InstanceVsLocal {
    public static void main(String[] args) {
        System.out.println("=== Instance vs Local Variables ===\n");

        Counter c1 = new Counter("Counter-A");
        Counter c2 = new Counter("Counter-B");

        c1.increment();
        c1.increment();
        c1.increment();

        c2.increment();

        System.out.println("\n=== Final State ===");
        c1.showCount();
        c2.showCount();

        System.out.println("\n=== Key Difference ===");
        System.out.println("Instance vars: belong to object, persist");
        System.out.println("Local vars: belong to method call, temporary");
    }
}

class Counter {
    // Instance variable - each Counter has its own
    String name;
    int count = 0;

    Counter(String name) {
        this.name = name;
    }

    void increment() {
        int oldCount = count;
        count++;
        System.out.println(name + ": " + oldCount + " → " + count);
    }

    void showCount() {
        System.out.println(name + " final count: " + count);
    }
}

Instance variables live with the object. Local variables die when the method returns.

instance variable Field that belongs to object, exists for object's lifetime.

Exercise: ClosureLambda.java

Explore effectively final and lambda variable capture