Skip to content

5.5. Variable Scope

Variable scope defines where in the program a variable can be accessed. Understanding scope is crucial for writing correct, maintainable code and avoiding naming conflicts.

Types of Variable Scope

1. Class Scope (Instance and Static Variables)

java
public class ScopeExample {
    // Instance variable - scope: entire class, each object has its own copy
    private String instanceVar = "Instance Variable";

    // Static variable - scope: entire class, shared among all objects
    private static String staticVar = "Static Variable";

    // Constants - usually static final
    public static final double PI = 3.14159;

    public void instanceMethod() {
        // Can access instance and static variables
        System.out.println(instanceVar);
        System.out.println(staticVar);
        System.out.println(PI);
    }

    public static void staticMethod() {
        // Can access only static variables
        // System.out.println(instanceVar); // Compilation error
        System.out.println(staticVar);
        System.out.println(PI);
    }
}

2. Method Scope (Local Variables)

java
public class MethodScope {

    public void demonstrateMethodScope() {
        // Local variable - scope: entire method
        String methodVar = "Method variable";

        if (true) {
            // Can access method-level variables
            System.out.println(methodVar);

            // Block-level variable
            String blockVar = "Block variable";
            System.out.println(blockVar);
        }

        // System.out.println(blockVar); // Compilation error - out of scope
        System.out.println(methodVar); // OK - still in scope
    }

    public void anotherMethod() {
        // Can declare variable with same name as in other method
        String methodVar = "Different method variable";
        System.out.println(methodVar);
    }
}

3. Block Scope

java
public class BlockScope {

    public void demonstrateBlockScope() {
        // Method scope variable
        String outerVar = "Outer variable";

        // Block 1
        {
            String block1Var = "Block 1 variable";
            System.out.println(outerVar); // OK - outer scope
            System.out.println(block1Var); // OK - same block
        }

        // Block 2
        {
            String block2Var = "Block 2 variable";
            System.out.println(outerVar); // OK - outer scope
            // System.out.println(block1Var); // Error - different block
            System.out.println(block2Var); // OK - same block

            // Can shadow outer variables
            String outerVar = "Shadowed variable"; // This shadows the outer variable
            System.out.println("Inner outerVar: " + outerVar);
        }

        System.out.println("Outer outerVar: " + outerVar); // Original value unchanged
    }

    public void loopScope() {
        for (int i = 0; i < 3; i++) {
            String loopVar = "Loop iteration " + i;
            System.out.println(loopVar);
        }

        // System.out.println(i); // Error - i is out of scope
        // System.out.println(loopVar); // Error - loopVar is out of scope
    }
}

Scope Hierarchy and Shadowing

java
public class ScopeHierarchy {
    // Class level
    private String name = "Class Level";
    private int value = 100;

    public void demonstrateShadowing() {
        // Method level - shadows class level 'name'
        String name = "Method Level";
        System.out.println("Method name: " + name);
        System.out.println("Class name: " + this.name); // Use 'this' to access class level

        // Block level
        {
            // Shadows both class and method level
            String name = "Block Level";
            int value = 200; // Shadows class level 'value'

            System.out.println("Block name: " + name);
            System.out.println("Block value: " + value);
            System.out.println("Class value: " + this.value); // Use 'this' for class level
        }

        System.out.println("Method name after block: " + name);
        System.out.println("Method sees class value: " + value); // Sees class level
    }

    // Parameter scope
    public void methodWithParams(String name, int value) {
        // Parameters shadow class level variables
        System.out.println("Parameter name: " + name);
        System.out.println("Parameter value: " + value);
        System.out.println("Class name: " + this.name);
        System.out.println("Class value: " + this.value);
    }
}

Practical Scope Examples

Bank Account with Proper Scope

java
public class BankAccount {
    // Class scope - encapsulated data
    private String accountNumber;
    private double balance;
    private String accountHolder;

    // Constructor parameters have method scope
    public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
        // 'this' distinguishes class variables from parameters
        this.accountNumber = accountNumber;
        this.accountHolder = accountHolder;

        // Validate and set balance
        if (initialBalance >= 0) {
            this.balance = initialBalance;
        } else {
            this.balance = 0;
            System.out.println("Warning: Negative initial balance set to 0");
        }
    }

    public void deposit(double amount) {
        // Method scope variable
        double oldBalance = this.balance;

        if (amount > 0) {
            this.balance += amount;
            System.out.printf("Deposited $%.2f. Balance: $%.2f → $%.2f%n",
                            amount, oldBalance, this.balance);
        } else {
            System.out.println("Deposit amount must be positive");
        }
    }

    public void withdraw(double amount) {
        if (amount > 0) {
            // Block scope inside if statement
            double potentialBalance = this.balance - amount;

            if (potentialBalance >= 0) {
                this.balance = potentialBalance;
                System.out.println("Withdrawn: $" + amount);
            } else {
                System.out.println("Insufficient funds for withdrawal: $" + amount);
            }
        }
    }

    // Static method - can only access static variables
    private static int accountCount = 0;

    public static int getAccountCount() {
        return accountCount;
    }
}

Temperature Converter with Scope Management

java
public class TemperatureConverter {
    // Class constants - static final, UPPERCASE naming convention
    public static final double ABSOLUTE_ZERO_C = -273.15;
    public static final double ABSOLUTE_ZERO_F = -459.67;

    // Instance variable to track conversion count
    private int conversionCount;

    public TemperatureConverter() {
        this.conversionCount = 0;
    }

    public double celsiusToFahrenheit(double celsius) {
        // Validate input
        if (celsius < ABSOLUTE_ZERO_C) {
            throw new IllegalArgumentException("Temperature below absolute zero: " + celsius);
        }

        // Method scope variables
        double fahrenheit = (celsius * 9 / 5) + 32;
        conversionCount++; // Modify instance variable

        return fahrenheit;
    }

    public double fahrenheitToCelsius(double fahrenheit) {
        if (fahrenheit < ABSOLUTE_ZERO_F) {
            throw new IllegalArgumentException("Temperature below absolute zero: " + fahrenheit);
        }

        double celsius = (fahrenheit - 32) * 5 / 9;
        conversionCount++;

        return celsius;
    }

    public void printConversionTable(int startC, int endC) {
        // Method scope
        System.out.println("Celsius to Fahrenheit Conversion Table");
        System.out.println("=====================================");

        // Loop scope - 'celsius' is only accessible within the loop
        for (int celsius = startC; celsius <= endC; celsius++) {
            // Block scope inside loop
            double fahrenheit = celsiusToFahrenheit(celsius);
            System.out.printf("%d°C = %.1f°F%n", celsius, fahrenheit);
        }

        // celsius is out of scope here
        System.out.println("Total conversions: " + conversionCount);
    }

    // Static method - no access to instance variables
    public static boolean isValidCelsius(double celsius) {
        return celsius >= ABSOLUTE_ZERO_C;
    }

    public static boolean isValidFahrenheit(double fahrenheit) {
        return fahrenheit >= ABSOLUTE_ZERO_F;
    }
}

Common Scope Issues and Solutions

Variable Shadowing Issues

java
public class ScopeIssues {
    private int data = 10;

    public void shadowingProblem(int data) {
        // Parameter 'data' shadows instance variable 'data'
        data = data + 5; // Modifies parameter, not instance variable
        System.out.println("Parameter data: " + data);
        System.out.println("Instance data: " + this.data); // Unchanged
    }

    public void solutionWithThis(int data) {
        this.data = data + 5; // Modifies instance variable
        System.out.println("Instance data now: " + this.data);
    }

    public void loopVariableIssue() {
        for (int i = 0; i < 3; i++) {
            System.out.println("Iteration: " + i);
        }

        // Cannot access 'i' here - out of scope
        // System.out.println("Final i: " + i); // Compilation error

        // Solution: declare variable outside loop if needed
        int i;
        for (i = 0; i < 3; i++) {
            System.out.println("Iteration: " + i);
        }
        System.out.println("Final i: " + i); // Now accessible
    }
}

Scope Best Practices Example

java
public class ScopeBestPractices {
    // Class variables - use meaningful names, keep private
    private String userName;
    private int userAge;
    private static final int MAX_AGE = 150;

    // Method with clear parameter names
    public void setUserData(String userName, int userAge) {
        // Use 'this' to distinguish parameters from instance variables
        this.userName = userName;

        // Validate input in method scope
        if (userAge >= 0 && userAge <= MAX_AGE) {
            this.userAge = userAge;
        } else {
            throw new IllegalArgumentException("Invalid age: " + userAge);
        }
    }

    // Keep method variables minimal and close to usage
    public void processUserData() {
        // Declare variables when they're needed
        String greeting = "Hello, " + this.userName;
        System.out.println(greeting);

        // Use block scope for temporary calculations
        {
            int yearsToRetirement = 65 - this.userAge;
            if (yearsToRetirement > 0) {
                System.out.println("Years to retirement: " + yearsToRetirement);
            }
        }
        // yearsToRetirement is out of scope here

        // Avoid long-lived method variables
        processInBlocks();
    }

    private void processInBlocks() {
        // Process in separate blocks to limit variable scope
        {
            int tempCalculation = this.userAge * 2;
            System.out.println("Temporary calculation: " + tempCalculation);
        }

        {
            String tempMessage = "User is " + this.userAge + " years old";
            System.out.println(tempMessage);
        }
        // Both tempCalculation and tempMessage are out of scope
    }

    // Use final for variables that shouldn't change
    public void calculateWithConstants() {
        final double TAX_RATE = 0.08;
        final int MAX_ITEMS = 10;

        double subtotal = 100.0;
        double tax = subtotal * TAX_RATE;

        System.out.println("Tax: $" + tax);

        // TAX_RATE = 0.09; // Compilation error - cannot reassign final
    }
}

Scope Best Practices

  1. Minimize variable scope: Declare variables as close to usage as possible
  2. Avoid shadowing: Use meaningful names to avoid conflicts
  3. Use 'final' for constants: Makes intent clear and prevents modification
  4. Keep methods small: Reduces the number of variables in scope
  5. Use blocks to limit scope: {} can create temporary scopes
  6. Initialize variables: Always initialize local variables before use
java
public class GoodScopePractice {
    private final int MAX_SIZE = 100; // Class constant

    public void goodMethod(String input) {
        // Initialize when declaring
        int count = 0;
        StringBuilder result = new StringBuilder();

        // Use blocks to limit scope of temporary variables
        {
            String trimmed = input.trim();
            result.append(trimmed);
        }
        // trimmed is out of scope here

        // Final for loop variables that shouldn't change
        for (final char c : input.toCharArray()) {
            if (Character.isLetter(c)) {
                count++;
            }
        }

        System.out.println("Letter count: " + count);
        System.out.println("Result: " + result);
    }
}