[Java] 變數的範圍與類型
一個變數代表了一個可以由程式所獨佔的儲存空間,除了鬆散型態程式語言之外的所有語言,都以資料型態明確的定義了每一個變數的大小及功能。
以上所說的應該對大家來說都早已是老生常談,但是你知道嗎?其實它也有很多不同的類型喔!
區域變數 (Local variables):
我要先強調,Java 的定義上沒有一般常見的全域變數(Global Variable),但提供了其他方法來達到原本的功能(例如static),
- methods, constructors, or blocks區域變數通常宣告在 方法(method)、建構子(constructor)或 區塊(block)之中
- 區域變數通常在進入 方法(method)、建構子(constructor)或 區塊(block)時建立,並在離開時自動銷毀
- 你無法在區域變數上使用存取修飾子
- 區域變數無法在所屬的 方法(method)、建構子(constructor)或 區塊(block)外使用
- 因為區域變數沒有預設值,所以使用者必須在使用前設定其數值(否則會出現 not have been initialized 的訊息)
- 一個 Java 方法(method)通常會使用區域變數來暫時儲存資料
以下是一個狹義區域變數的範例,將變數宣告在方法(method)之中
public class Test{ public void pupAge(){ int age = 0; age = age + 7; System.out.println("Puppy age is : " + age); } public static void main(String args[]){ Test test = new Test(); test.pupAge(); } }
結果:
Puppy age is: 7
接下來我們做一個小實驗,若將以上範例中的
int age=0;
換成這個呢?
int age;
答案是你會得到一個編譯時期錯誤,像這樣:
Test.java:4:variable number might not have been initialized age = age + 7; ^ 1 error
因為 沒有初始化 age 的數值,會造成讀不到數值的情況 (NULL),所以 compiler 先提醒使用者。
實例變數、非靜態屬性變數(Instance variables、Non-Static Fields):
技術上來說,物件會將其個別狀態儲存在”非靜態區”,也就是沒有宣告 static 的屬性變數的形式。
- 實例變數會被宣告在類別(class)之中,但在 方法(method)、建構子(constructor)或 區塊(block)外
- 當物件被使用 new 的方式建立,對應的實例變數也會被建立(匿名物件除外),當物件被清除,變數也一同被清除
- 實例變數可以使用存取修飾子
- 實例變數對所有 方法(method)、建構子(constructor)或 區塊(block)而言都是可見的,建議使用存取修飾子來調整權限
- 有預設值, 如果沒有給訂初值, 編譯器也會給予預設值(數字型態為0、字元型態為NULL、布林型態為false
- 若在同一類別中,實例變數可以直接呼叫其名稱存取,但若在靜態方法或其他類別之中則須改用 <物件參照>.<變數名稱> 的方式來存取
底下以範例讓大家認識它的特性:
public class Employee { // 實例變數 name 對所有的子類別都是可見的 public String name; // 實例變數 salary 只在 Employee 類別是可見的 // salary variable is visible in Employee class only. private double salary; // 實例變數 name 的值在這個建構子中被指定 public Employee (String empName) { name = empName; } // 實例變數 salary 的值在這個方法中被指定 public void setSalary(double empSal) { salary = empSal; } // 印出資訊 public void printEmp() { System.out.println("name : " + name ); System.out.println("salary :" + salary); } public static void main(String args[]) { Employee empOne = new Employee("Ransika"); empOne.setSalary(1000); empOne.printEmp(); } }
結果:
name : Ransika salary :1000.0
靜態屬性變數 (Class Variables、Static Fields):
static int age;
沒錯,靜態(static),一開始就被載入記憶體,並且放在特別的地方(靜態區),給了它不一樣的特性:
- 靜態變數在類別檔載入時就已經初始化
- 靜態變數在任何該類別的物件被建立(常用 new)之前就已經被初始化
- 靜態變數在任何該類別的靜態方法執行之前就已經被初始化
- 有預設值, 如果沒有給訂初值, 編譯器也會給予預設值(數字型態為0、字元型態為NULL、布林型態為false)
我們可以用以下的範例來進一步熟悉它的特性:
class VariableDemo { static int count=0; public void increment() { count++; } public static void main(String args[]) { VariableDemo obj1=new VariableDemo(); VariableDemo obj2=new VariableDemo(); obj1.increment(); obj2.increment(); System.out.println("Obj1: count is="+obj1.count); System.out.println("Obj2: count is="+obj2.count); } }
輸出:
Obj1: count is=2 Obj2: count is=2
從上面的範例中我們可以發現,雖然是透過不同的物件中的 increment() 操作,但實際上操作的卻是同一區塊,證明 static 變數不是跟其他同 class 的元素一起放在堆積區,而是獨立放在靜態區,因此 obj1 和 obj2 的 count 都是指向記憶體的同一區塊。可以參考下圖(來源:yhhuang1966.blogspot.tw/2014/03/java_25.html)
再來,我們再用一個範例示範一下 static 的存取特性:
public class main { public static void main(String[] arg) { Variable v1 = new Variable(10); v1.printA(); Variable v2 = new Variable(20); System.out.println("b= " + v2.b); } } class Variable { int a; static int b; public Variable(int x) { a=x; } public void printA() { System.out.println("a= " + a); } }
結果:
a= 10 b= 0
參數(Parameters):
放在函式的標記式,用來說明這個函式,當它被呼叫時必須接收到什麼樣的資料(若跟小獅一樣會跟引數(Arguments)搞混的人,請參考這篇文章)
void doStuff(String s, int a) {} // 我們預期兩個參數: String 和 int
來個範例:
public class MyClass { static int a; int b; public static void myMethod(int c) { try { int d; } catch (Exception e){} } MyClass(int f) { int[] g = new int[100]; } public static void main(String[] arg){} // 略 }
- 例外處理參數(exception-handler parameter):宣告在 catch 小括號內的變數,例如上面程式碼的 e。
- 建構子參數(constructor parameter):宣告在 constructor 小括號內的變數,例如上面程式碼的 f。