[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。
變數的初始化:
區域變數 (Local variables) | 必須手動初始化 |
實例變數(Instance variables、Non-Static Fields)靜態屬性變數 (Class Variables、Static Fields)通稱 屬性變數(Field Variable) | 若未指定 Java 會給予預設值 |
變數預設值:
byte | 0(byte 型態) |
short | 0(short 型態) |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0D |
char | ‘u0000’(NULL) |
boolean | false |
同場加映:
因為 variable shadowing 在 Java 中是不存在的,所以以下的範例二會無法編譯!
class X { public static void main(String args[]) { { int a = 2; } { int a = 3; } } }
▲編譯成功
class X { public static void main(String args[]) { int a = 2; { int a = 3; } } }
▲編譯失敗
但有趣的是,你可以在如下的範例中找到 variable shadowing 的”影子”
class A { int x = 0; void m() { int x = 10; // Shadows this.x } }
參考資料(Reference):
- Java Variable Types – http://www.tutorialspoint.com/java/java_variable_types.htm
- 變數的種類 – http://www.oreilly.com.tw/column_sleepless.php?id=j015
- 基礎程式設計(13)-變數範圍 – http://www.moke.tw/wordpress/computer/programming/337
- Variables (The Java™ Tutorials) – http://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html
- java – Block scope variables – Stack Overflow – http://stackoverflow.com/questions/20499554/block-scope-variables
- Java – static variable with example – http://beginnersbook.com/2013/05/static-variable/
- 小狐狸事務所: Java 複習筆記 : 變數與記憶體 – http://yhhuang1966.blogspot.tw/2014/03/java_25.html
- ThiS WORLD, MY WORLD: 引數?! 參數??!! 什麼鬼啊!! – http://thisworldmyworld.blogspot.tw/2009/12/blog-post_08.html