深刻理解單例七種模式(一)

最近剛看了單例模式,由於以前只在大話上看過就兩種簡單實現,也把網上介紹的給實踐一下,而後對比一下JDK裏面有類似的實現,簡單講一下個人理解,歡迎批評哈:java

餓漢模式:安全

 1 public class Singleton {
 2 
 3     private static final Singleton instance = new Singleton();
 4 
 5     private Singleton() {
 6     }
 7 
 8     public static Singleton getInstance() {
 9         return instance;
10     }
11     
12     
13 }
14     

優勢:簡單直觀,線程安全,在類加載的時候就直接初始化了ide

缺點:寫死的方式不夠靈活,有多個classload的時候也會產生多個實例,並且不夠lazy loading。函數

JDK的例子(java.lang.Runtime):測試

1 public class Runtime {
2     private static Runtime currentRuntime = new Runtime();
3 
4     public static Runtime getRuntime() {
5         return currentRuntime;
6     }
7 
8     /** Don't let anyone else instantiate this class */
9     private Runtime() {}

 下面基於類加載機制寫了個簡單的demo看下這種餓漢模式存在的問題(代碼稍作修改):this

參考:spa

http://my.oschina.net/pingpangkuangmo/blog/376328.net

 1 public class Singleton {
 2 
 3     private static final Singleton instance = new Singleton();
 4 
 5     private Singleton() {
 6         System.out.print("類加載:");
 7         System.out.println(this.getClass().getClassLoader());
 8     }
 9 
10     public static Singleton getInstance() {
11         return instance;
12     }
13     
14 }

下面仿照網上的例子寫了個簡單的類加載器:線程

 1 import java.io.ByteArrayOutputStream;
 2 import java.io.FileInputStream;
 3 
 4 public class MyclassLoader extends ClassLoader {
 5     
 6     private String name;
 7     private String classPath;
 8     
 9     public MyclassLoader(String name){
10         super(null);
11         this.name = name;
12     }
13     
14     @Override
15     public Class<?> findClass(String name){
16         byte[] by=getClassBytes(name);         
17         return this.defineClass("service.Singleton",by,0,by.length);
18     }
19     
20     private byte[] getClassBytes(String name) { 
21         byte[] data = null;
22         String classFullPath=name;
23         try {  
24             FileInputStream in=new FileInputStream(classFullPath);  
25             ByteArrayOutputStream out=new ByteArrayOutputStream(); 
26             int i = 0;  
27             while ((i = in.read()) != -1) {  
28                 out.write(i);  
29             }  
30             data = out.toByteArray();
31         } catch (Exception e) {  
32             e.printStackTrace();  
33         } 
34         return data;    
35     }
36     
37     public String getName() {  
38         return name;  
39     }  
40   
41     public void setName(String name) {  
42         this.name = name;  
43     }  
44   
45     public String getClassPath() {  
46         return classPath;  
47     }  
48   
49     public void setClassPath(String classPath) {  
50         this.classPath = classPath;  
51     }  
52 }

下面是測試程序:3d

 1     public static void main(String[] args) {
 2         Singleton singleton_1 = Singleton.getInstance();
 3         Singleton singleton_2 = Singleton.getInstance();
 4         System.out.println("正常類加載單例實例比較:"+"{"+singleton_1+":"+singleton_2+""+"}-->"+singleton_1.equals(singleton_2));
 5         System.out.println("正常加載類信息比較:"+"{"+singleton_1.getClass()+":"+singleton_2.getClass()+""+"}-->"+singleton_1.getClass().equals(singleton_2.getClass()));
 6         MyclassLoader myClassLoader1=new MyclassLoader("myClassLoader1");  
 7         MyclassLoader myClassLoader2=new MyclassLoader("myClassLoader2");  
 8         
 9         Class singletonClass1 = myClassLoader1.findClass("H:/totalwork/exerse/target/classes/service/Singleton.class");
10         Class singletonClass2 = myClassLoader2.findClass("H:/totalwork/exerse/target/classes/service/Singleton.class");
11         
12         System.out.println("單獨加載器類信息:"+"{"+singletonClass1+":"+singletonClass2+""+"}-->"+singletonClass1.equals(singletonClass2));
13         
14         try {
15             Constructor constructor_1=singleton_1.getClass().getDeclaredConstructor();
16             Constructor constructor_2=singleton_2.getClass().getDeclaredConstructor();
17             System.out.println("正常類加載單例構造器比較:"+"{"+constructor_1+":"+constructor_2+""+"}-->"+constructor_1.equals(constructor_2));
18 
19             Constructor constructor1=singletonClass1.getDeclaredConstructor();
20             Constructor constructor2=singletonClass2.getDeclaredConstructor();
21             System.out.println("單獨加載器構造器比較:"+"{"+constructor1+":"+constructor2+""+"}-->"+constructor1.equals(constructor2));
22             
23             System.out.println("-----------設置權限:start------------");
24             constructor_1.setAccessible(true);
25             constructor_2.setAccessible(true);
26             System.out.println("-----------設置權限:end--------------");
27             
28             Object obj_1=constructor_1.newInstance();
29             Object obj_2=constructor_2.newInstance();
30             System.out.println("正常類加載單例生成實例比較:"+"{"+obj_1.hashCode()+":"+obj_2.hashCode()+""+"}-->"+obj_1.equals(obj_2));
31             
32             System.out.println("-----------設置權限:start------------");
33             constructor1.setAccessible(true);
34             constructor2.setAccessible(true);
35             System.out.println("-----------設置權限:end--------------");
36             
37             Object obj1=constructor1.newInstance();
38             Object obj2=constructor2.newInstance();
39             
40             System.out.println("單獨加載器單例生成實例比較:"+"{"+obj1+":"+obj2+""+"}-->"+obj1.equals(obj2));
41             
42         } catch (Exception e) {
43             e.printStackTrace();
44         } 
45         
46     }

下面是運行結果:

 1 類加載:sun.misc.Launcher$AppClassLoader@fb56b1
 2 正常類加載單例實例比較:{service.Singleton@290fbc:service.Singleton@290fbc}-->true
 3 正常加載類信息比較:{class service.Singleton:class service.Singleton}-->true
 4 單獨加載器類信息:{class service.Singleton:class service.Singleton}-->false
 5 正常類加載單例構造器比較:{private service.Singleton():private service.Singleton()}-->true
 6 單獨加載器構造器比較:{private service.Singleton():private service.Singleton()}-->false
 7 -----------設置權限:start------------
 8 -----------設置權限:end--------------
 9 類加載:sun.misc.Launcher$AppClassLoader@fb56b1
10 類加載:sun.misc.Launcher$AppClassLoader@fb56b1
11 正常類加載單例生成實例比較:{13228332:30472956}-->false
12 -----------設置權限:start------------
13 -----------設置權限:end--------------
14 類加載:service.MyclassLoader@1bf3d87
15 類加載:service.MyclassLoader@1bf3d87
16 類加載:service.MyclassLoader@1e4f7c2
17 類加載:service.MyclassLoader@1e4f7c2
18 單獨加載器單例生成實例比較:{service.Singleton@10dc6b5:service.Singleton@170bea5}-->false

一、能夠看到MyclassLoader是咱們本身的類加載器,自定義classloader咱們須要經過繼承java.lang.ClassLoader來實現,其中private byte[] getClassBytes(String name)函數,就是經過文件流傳輸講.class字節碼讀到byte[] data中。

二、而後交給classloader的defineClass1來實現加載,從源碼能夠看出來

1     protected final Class<?> defineClass(String name, byte[] b, int off, int len)
2         throws ClassFormatError
3     {
4         return defineClass(name, b, off, len, null);
5     }

...

1     private native Class defineClass1(String name, byte[] b, int off, int len,
2                                       ProtectionDomain pd, String source);

其中:

一、singleton_1和singleton_2爲直接產生的單例對象,咱們也能夠看到指向同一個對象。也能夠看到他們默認爲sun.misc.Launcher$AppClassLoader@fb56b1加載器加載。

二、myClassLoader1和myClassLoader2是咱們構造的兩個類加載器。

三、singletonClass1和singletonClass2分別是兩個構造器構造的Class對象。其實對於任意一個類,都須要由加載它的類加載器和這個類自己一同確立其在java虛擬機的惟一性,每個類加載器,都擁有一個獨立的類命名空間。通俗表達:比較兩個類是否「相等」,只有在這兩個類由同一個類加載器加載的前提下才有意義,不然,即便這兩個類來源於同一個class文件,被同一個虛擬機加載,只要加載它們的類加載器不一樣,那這兩個類一定不相等(摘自深刻理解Java虛擬機)。

經過上面的解釋,下面幾個對象比較的結果其實就能夠理解了。

四、constructor_1和constructor_2爲單例對象class獲得的,constructor1和constructor2爲加載類獲得的。

其實有兩種方法能夠執行類的構造函數: 

Class.newInstance() 只可以調用無參的構造函數,即默認的構造函數, 不過要求構造函數爲public,上面的單例會直接拋出java.lang.IllegalAccessException異常。
Constructor.newInstance() 能夠根據傳入的參數,調用任意構造構造函數。

五、從結果來看constructor_1和constructor_2是equal的,constructor1和constructor2壓根沒有可比性,因此確定爲false。

先寫這麼多,回頭再補充吧。

這裏還有幾個疑問沒解決,知道的但願幫我解答一下:

一、constructor_1和constructor_2分別new出來的實例obj_1和obj_2爲何不相等?我查看了一下它們倆的hashCode,確實不同,可是它們兩個是同一個系統類加載器加載的,有點搞不懂。

二、Object obj1=constructor1.newInstance();這個語句執行了兩次無參構造函數,我單步發現obj1生成的過程當中,先執行了無參構造函數再執行的靜態成員初始化語句

private static final Singleton instance = new Singleton();誰能告訴我緣由呢?

 感謝網友回答:對於第一個問題,這兩個對象本應該就是不同的,一旦能用反射,就沒有單例可言了,使用枚舉能夠阻止反射。

對於第二個問題,就是在加載類的時候就會去執行static方法,執行的時候發現是new 自己,就去執行構造函數了。類加載完成以後,緊接着執行constructor3.newinstance方法,又會執行一次構造方法,因此會執行兩次。因此對於系統默認類加載器只是把靜態成員初始化了而已,沒有再像newinstance這樣調用構造函數。

相關文章
相關標籤/搜索