[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。
