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
- Minimize variable scope: Declare variables as close to usage as possible
- Avoid shadowing: Use meaningful names to avoid conflicts
- Use 'final' for constants: Makes intent clear and prevents modification
- Keep methods small: Reduces the number of variables in scope
- Use blocks to limit scope: {} can create temporary scopes
- 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);
}
}