有些類不希望被執行個體化,比如一些工具類(隻包含static方法和域),執行個體化沒有任何意義。為了防止這樣的類被執行個體化,不寫構造函數是沒有用的,因為預設的無參構造器(編譯器自動生成)可以用。
同時,把類寫成抽象類不可行。原因:
1. 繼承之後可以執行個體化;
2. 更糟糕的是,還有可能讓使用者覺得這是為了繼承設計的。
解決方案:
加上私有構造器,并在方法體内部抛出異常(不是必須但是有效);最好再加上注釋,表明這個類不應該被執行個體化。
于是我就特意檢視Arrays的源碼發現:
// Suppresses default constructor, ensuring non-instantiability.
private Arrays() {}
還有Math:
/**
* Don't let anyone instantiate this class.
*/
private Math() {}
當然,這樣的副作用就是無法繼承,因為要生成子類父類必須先構造,而這樣的類的構造器無法調用。
…
…
你以為這樣就安全了嗎?
太天真了…
我從Thinking in Java中看到Bruce Eckel在類型資訊這一章最後一節強行反射調用了内部類方法,匿名内部類方法,private方法。而想要調用private方法的關鍵就是:
setAccessible(true);
是以最後實作的關鍵代碼就是(異常已經在main中直接抛出):
//擷取構造函數,這裡隻有一個無參構造器
Constructor con=Math.class.getDeclaredConstructor();
//沒有這句話會抛出IllegalAccessException
con.setAccessible(true);
//構造和強轉
Math math=(Math) con.newInstance();
//驗證
System.out.println(math.PI);
輸出結果是:3.141592653589793
當然,在Math類有構造器裡抛出異常的話…像這樣:
/**
* Don't let anyone instantiate this class.
*/
private Math() {throw new AssertionError()}
依然沒有用,相應的代碼隻需要把構造部分try catch不做任何處理就行。
//擷取構造函數,這裡隻有一個無參構造器
Constructor con=Math.class.getDeclaredConstructor();
//沒有這句話會抛出IllegalAccessException
con.setAccessible(true);
//構造和強轉
try{
Math math=(Math) con.newInstance();
System.out.println(math.PI);
}catch(Exception e){
//do nothing
}
……
當然,如果你都花這麼大力氣去構造了,還有什麼理由要阻止你呢?畢竟在懸崖邊建立護欄隻是為了防止意外,你非要跳下去也許是為了飛起來吧。
:-P