Java 變數範圍 (Scope)

變數範圍決定變數在哪裡可以被存取。

區域變數 (Local Variables)

在方法、建構子或區塊內宣告的變數:

public void method() {
    int x = 10;  // 區域變數
    System.out.println(x);  // 可以存取
}  // x 在這裡結束

// System.out.println(x);  // 錯誤!x 不存在

區塊範圍

public void test() {
    int a = 1;
    
    if (true) {
        int b = 2;  // 區塊內的區域變數
        System.out.println(a);  // 可以存取外層的 a
        System.out.println(b);  // 可以
    }
    
    System.out.println(a);  // 可以
    // System.out.println(b);  // 錯誤!b 不存在
}

迴圈變數

for (int i = 0; i < 5; i++) {
    System.out.println(i);  // 可以
}

// System.out.println(i);  // 錯誤!i 不存在

實例變數 (Instance Variables)

在類別內、方法外宣告的變數,屬於物件:

public class Person {
    private String name;  // 實例變數
    private int age;      // 實例變數
    
    public void setName(String name) {
        this.name = name;  // 使用 this 區分
    }
    
    public void printInfo() {
        System.out.println(name + ", " + age);
    }
}

每個物件有自己的實例變數:

Person p1 = new Person();
Person p2 = new Person();

p1.setName("Alice");
p2.setName("Bob");
// p1 和 p2 有各自的 name

類別變數 (Class Variables)

使用 static 宣告的變數,屬於類別,所有物件共享:

public class Counter {
    private static int count = 0;  // 類別變數
    
    public Counter() {
        count++;
    }
    
    public static int getCount() {
        return count;
    }
}

Counter c1 = new Counter();  // count = 1
Counter c2 = new Counter();  // count = 2
Counter c3 = new Counter();  // count = 3

System.out.println(Counter.getCount());  // 3

變數遮蔽

區域變數可以和實例變數同名,會遮蔽實例變數:

public class Example {
    private int value = 10;  // 實例變數
    
    public void method() {
        int value = 20;  // 區域變數,遮蔽實例變數
        
        System.out.println(value);       // 20(區域變數)
        System.out.println(this.value);  // 10(實例變數)
    }
}

參數遮蔽

public class Person {
    private String name;
    
    public void setName(String name) {
        // 參數 name 遮蔽了實例變數 name
        this.name = name;  // 使用 this 指向實例變數
    }
}

變數生命週期

變數類型建立時機銷毀時機
區域變數進入區塊離開區塊
實例變數物件建立物件被 GC
類別變數類別載入類別卸載

存取修飾詞

控制變數的可見範圍:

public class Example {
    public int publicVar;      // 任何地方都可存取
    protected int protectedVar;// 同套件和子類別可存取
    int defaultVar;            // 同套件可存取
    private int privateVar;    // 只有本類別可存取
}

常見錯誤

使用未初始化的區域變數

public void method() {
    int x;
    // System.out.println(x);  // 編譯錯誤!
    
    x = 10;
    System.out.println(x);  // OK
}

在錯誤的範圍存取變數

public void method() {
    if (true) {
        int x = 10;
    }
    // System.out.println(x);  // 錯誤!x 不在範圍內
}

混淆區域變數和實例變數

public class Example {
    private int value = 100;
    
    public void confusing(int value) {
        // 這裡的 value 是參數,不是實例變數
        System.out.println(value);       // 參數值
        System.out.println(this.value);  // 100
    }
}

最佳實踐

  1. 最小範圍原則:變數範圍越小越好
  2. 避免變數遮蔽:除非有明確理由
  3. 及時初始化:宣告時就初始化
  4. 有意義的命名:避免混淆
// 好的做法
for (int i = 0; i < arr.length; i++) {
    int current = arr[i];  // 最小範圍
    // 使用 current
}

// 不好的做法
int i;  // 範圍太大
int current;
for (i = 0; i < arr.length; i++) {
    current = arr[i];
    // ...
}