Constructor 和 Static Initializer 的差別

前言

泰勞因為接觸Android才開始學習用Java寫程式,有很多基本觀念並沒有在一開始就建立起來,因此開發過程中總是曲折離奇,時常耗費過多的時間來處理語法或是愚蠢的錯誤。雖然說做中學並沒有不好,而錯中學對工程師而言本來就是該有的態度,但若是能有扎實的基本觀念來支撐,一定能讓專案開發的更順利更完美!

建構子(Constructor)

在Java裡有個特殊的方法叫做建構子,Construct這個動詞本身就有建造、建構的意思,動詞後面加上er或or變成名詞,則有的意思,例如:teach是,teacher是教師;pitch是,pitcher是投手等等,對此有興趣的可以去翻閱高如峯著作的神奇的字元素因此Constructor在這裡可解讀為建構某些東西的方法。

特性

Constructors are special methods invoked when an object is created and are used to initialize them. A constructor name must be same as its class name.
建構子是當物件被建立(new)時,用於初始化物件屬性的方法。建構子的名稱必須和其類別名稱相同。

public class Program
{

    //建構子
    Program(){
        System.out.println("Constructor");
    }
    public static void main(String[] args) {

        //建立Program物件,並取名為p1
        Program p1 = new Program();
    }
}

上面的範例定義了Program類別用於初始化物件的建構子,並在main方法裡建立一個Program物件,此時Program類別裡的建構子就會被執行,因此會輸出Constructor這個字串。那如果建立兩個Program物件呢?

public static void main(String[] args) {
        Program p1 = new Program();
        Program p2 = new Program();
}

根據定義,建構子在物件被建立時就會執行,因此上面這段程式建立了兩個Program物件,建構子就會執行兩次,所以最後會輸出兩次Constructor字串。
一個類別裡,可以擁有多個不同參數的建構子。上面的範例是沒有參數的版本,當然也可以定義需要參數的建構子,若要使用,就必須在建立物件的同時,將參數帶入,這樣Java才能判斷要用哪一個建構子來初始化物件。

public class Program
{
    //建構子
    Program(){
        System.out.println("Constructor");
    }
    Program(String name){
        System.out.println("Name:"+name);
    }
    Program(int age){
        System.out.println("Age:"+age);  
    }
    public static void main(String[] args) {
        Program p1 = new Program();
        Program p2 = new Program("Kuanlin");
        Program p3 = new Program(18);
    }
}

上面範例程式輸出的結果如下圖所示,Java會依照不同的參數建構出不一樣的Program物件。
Static Initializer:

在Java裡有個功能與建構子非常相似,它就是Static Initializer,有時也會稱它為Class Initializer,因為它扮演的角色與類別可說是息息相關。Static Initializer必須使用Static關鍵字來修飾,而寫在Static區塊裡的程式,會習慣以Static Block來稱呼。

特性

Static Initializer executed once at the time of class-loading and initialized by JVM. It can be thought of as a "Class Constructor".
Static Initializer的功能在於初始化類別,當類別初次被使用被載入至JVM時,會執行寫在Static Block裡的程式碼。舉個例子,載入外部的函式庫,就是個常見的使用時機,如下範例所示。

public class Program
{
    //Static Initializer
    static{
        System.loadLibrary("myLib");
    }
    ......
}

定義中特別提到可以將Static Initializer視為Class Constructor,若直接翻譯成中文就是類別的建構子(此翻譯無文獻,大家參考就好)。對比文章開頭那個用於初始化物件的建構子,它們兩之間的功能非常類似,但目的卻不一樣喔!

Static InitializerVS.Constructor
靜態初始化區塊中文翻譯建構子
類別初始化目標物件
類別被載入至JVM初始化時機物件被建立(new)
需使用Static關鍵字來修飾特殊要求建構子名稱需與其類別名稱相同

接著來看一個範例,在類別裡定義Static Initializer與Constructor,並在main方法裡建立兩個物件,觀察看看到底會輸出什麼結果。

public class Program
{
    static{
        System.out.println("Static Block");
    }
    Program(){
        System.out.println("Constructor");
    }
    public static void main(String[] args) {
        Program p1 = new Program();
        Program p2 = new Program();
    }

}

建議大家先想一想、動一動頭腦、泡一泡牛奶再看答案。

相信大家都答對了吧!只要把它們之間不同之處搞懂了,就不會覺得困難。後面還有一個有趣的範例,直接來看程式碼吧!

Program.java:
public class Program
{
    static{
        System.out.println("Static Block");
    }
    Program(){
        System.out.println("Constructor");
    }
    public static void main(String[] args) {
        System.out.println("Program");
        Program p1 = new Program();
        Program p2 = new Program();
    }
}

Test.java:
public class Test{
    public static void main(String[] args){
        System.out.println("Test");
        Program p3 = new Program();
        Program p4 = new Program();
    }
}

Test.java與Program.java置於同一個目錄底下,將程式編譯之後,分別執行兩個Java檔,會得到什麼結果呢?

$ javac Program.java
$ java Program

$ javac Test.java
$ java Test

有沒有發現Program與Test最關鍵的差別在哪裡呢?仔細思考就會理解,因為Program在自己的main方法裡建立Program物件時,Program類別的main方法已經被執行了,也就是說Program類別已經被JVM載入了。相較之下,Test在main方法裡建立Program物件時,Program類別才初次被使用,因此輸出字串的順序才會有些許不同。

留言

這個網誌中的熱門文章

程式語言常用之符號與詞彙 - 中英文對照

Repo 實用指令

什麼是 Bootloader?