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

![[Java] 引數?! 參數??!! 什麼鬼啊!!](;https://img.alexleo.click/teambob/java-coffee.png) 
																			![[小小測試] C/C++ 與 Java、struct 與 class 用法比較](;https://img.alexleo.click/teambob/chemical-reaction-24562_640.png) 
																			![[Linux] 如何安裝 Eclipse 在 Ubuntu 14.04](;https://img.alexleo.click/teambob/eclipse.png) 
																			 
																											 
																											![[Linux] 如何使用 LUKS 建立加密的磁碟映像檔](https://img.alexleo.click/Team-BoB_luks_disk_image/pixy.org_97715-small.jpg) 
																											![[筆記] Matplotlib 使用上的一些建議](https://img.alexleo.click/Team-BoB_matplotlib_notes/title.jpg) 
																											![[Git 筆記] merge、squash、rebase 三種方式的比較](https://img.alexleo.click/Team-BoB_git_merge_squash_rebase/cover.jpg) 
																											![[JavaScript] 手把手一起入門(二) – 變數 & 基本操作](;https://img.alexleo.click/teambob/WHATWG_JavaScript_logo.png) 
																											![[C/C++] Linklist 的架構與使用](;https://img.alexleo.click/teambob/2405613966_c68110ca76_o.jpg)