私有建構函式的應用與靜態工廠模式

mxdxm8899發表於2010-05-08

在Java中,建構函式的訪問級別通常是public, 它提供了一個構造該類物件的介面。可是你知不知道,把建構函式的級別設為private, 有一些特別用處。

先來看一段程式碼:
//Shape.java
public class Shape {
    private Shape() {
      
    }

    public static Shape makeShape() {
       System.out.println("here is the shape you ordered");
       return (new Shape());
    }

    public static void main(String args[]) {
         Shape.makeShape();
    }
}
首先從語言角度分析,我們可以知道, 任何類的使用者都無法使用建構函式來生成一個圖形, 因為建構函式是私有的,無法被類以外的函式使用。而只能通過呼叫makeShape來實現。

也許你會問,為什麼不直接使用建構函式來生成圖形,而需要使用一個看上去多餘的makeShape方法呢?

這樣做有以下幾個好處:

1。你可以返回任何的Shape型別,包括Shape的子類。比如你可以把makeShape寫成這樣:

       public static Shape makeShape() {
       System.out.println("here is the shape you ordered");
       if (retangle)
             return (new Retangle());
       if (Circle)
            return (new Circle());
   
    }

    這裡假設Retangle 和 Circle 都是shape的子類,並且和Shape類在同一個包內,Shape類可以訪問子類的建構函式。這樣shape就提供了一個圖形工廠。使用者通過一個介面就可以生成不同的圖形。事實上,這種用法被稱為“工廠模式”。

2。可以實現一個類只有一個物件。請看下面的程式碼

       //Handler.java
public class Handler {
   
    private Handler handler = null;
    private Handler() {
      
    }

    public static getHandler() {
        if (!handler)
             handler = new Handler();
       return handler;
    }

    public static void main(String args[]) {
         Handler.getHandler();
    }
}

當handlerw為空時,那麼重新構造一個handler,然後返回;如果已經構造過了,那麼就直接返回已經存在的handler。這種用法被稱為“Singleton pattern". 如果直接使用建構函式來構造物件,那麼你就無法控制生成的數量。在實際應用中,往往會做一些改變。比如使用一個具有一定容量的池,當需要構造一個物件而池的容量仍未滿時,就構造一個新的物件,並放入池中,並把物件的狀態設為“佔用”狀態;當需要構造一個物件而池的容量已滿,則從池中選一個“空閒”狀態的物件返回,並把物件的狀態設為“佔用”。當物件使用完後再回收到池中並把狀態設為“空閒“。

 這種模式的一個典型應用場景是:

    在一個具有很多使用者的web站點裡,需要一個物件來單獨處理一個連線,而每一個連線的時間比較短。如果每次連線都建立一個物件然後又很快銷燬,那麼建立和銷燬物件的系統開銷是很大的。這種時候可以使用物件池,這樣就免去了建立和銷燬物件的開銷。

3。可以方便的丟擲異常。請看下列程式碼:

       public class Test {
        public Test() {
                double x = 1.0/0.0;
        }
        public static void main(String args[]) {
        try {
                Test test = new Test();
        }catch (Exception e){
                System.out.println(e.toString());
        }
  }
}

編譯,執行,你會發現這個異常不會被捕捉,沒有任何輸出;即使嘗試在建構函式中捕捉異常也不行。看下列程式碼:

public class Test {
        public Test() {
                try {
                System.out.println("trying to throw an exception");
                double x = 1.0/0.0;
                } catch(Exception e) {
                        System.out.println("Exception captured");
                }finally {
                        System.out.println("inside finally");
                }
        }
        public static void main(String args[]) {
                Test test = new Test();
        }
}
編譯,執行,結果為:
trying to throw an exception
inside finally

原因是JVM把建構函式產生的異常丟棄了。試想你正在使用一個第三方的類庫提供的類,那個類提供一個共有的建構函式,它允許你通過引數構造一個類的物件,可是如果你的引數不合法,導致在建構函式中產生一個異常,那麼你永遠不知道具體發生了什麼。當然如可以在每次構造物件時進行引數合法性檢查,可是假設你要構造好多這樣的物件??那將是一場災難。這時可以通過把建構函式的訪問級別設為私有,強迫類的使用者使用一個工廠函式來生成需要的物件,那麼就可以在這個函式中統一的進行引數檢查了。具體的程式碼就不寫了,留給讀者去實踐吧!

從上面的分析我們可以知道私有建構函式的威力。需要注意的一點是,即使你的建構函式什麼都不做,比如:
private Shape() {}
你仍然要顯示的定義,因為如果你不定義,那麼Java會自動為你生成一個空建構函式,而這個空建構函式是共有的。

相關文章