今天和同事在項目中用了一下單例模式,閒下來瞭解了一下單例模式的實現方式。參考了 http://cantellow.iteye.com/blog/838473 這篇博客。瞭解到主要有懶漢模式和餓漢模式兩種。前者實現了咱們所知道的lazy load。 在這裏我選了兩種實現方式本身實現了一下。
public class Singleton { public static String NAME = "Singleton"; private static Singleton singleton = new Singleton(); private Singleton() { System.out.println("initial Singleton"); } public static Singleton getSingleton() { return singleton; } }
public class InnerSingleton { public static String NAME = "InnerSingleton"; private static class Singleton { private static final InnerSingleton INNER_SINGLETON = new InnerSingleton(); } private InnerSingleton() { System.out.println("initial InnerSingleton"); } public static InnerSingleton getSingleton() { return Singleton.INNER_SINGLETON; } }
看了類加載和類初始化的一些相關知識,參考 https://www.cnblogs.com/zhguang/p/3154584.html
無論使用什麼樣的類加載器,類都是在第一次被用到時,動態加載到JVM的。這句話有兩層含義: Java程序在運行時並不必定被完整加載,只有當發現該類尚未加載時,纔去本地或遠程查找類的.class文件並驗證和加載;當程序建立了第一個對類的靜態成員的引用(如類的靜態變量、靜態方法、構造方法——構造方法也是靜態的)時,纔會加載該類。Java的這個特性叫作:動態加載。
須要區分加載和初始化的區別,加載了一個類的.class文件,不覺得着該Class對象被初始化,事實上,一個類的初始化包括3個步驟:html加載(Loading),由類加載器執行,查找字節碼,並建立一個Class對象(只是建立); 連接(Linking),驗證字節碼,爲靜態域分配存儲空間(只是分配,並不初始化該存儲空間),解析該類建立所須要的對其它類的應用; 初始化(Initialization),首先執行靜態初始化塊static{},初始化靜態變量,執行靜態方法(如構造方法)。
類初始化的時機分如下幾種狀況java
使用new關鍵字實例化對象的時候
讀取或設置一個類的靜態字段(被final修飾,已在編譯期把結果放入常量池的靜態字段除外, 舉個例子?)
調用一個類的靜態方法
使用java.lang.reflect包的方法對類進行反射調用的時候
當初始化一個類的時候,若是發現其父類尚未進行初始化,須要先初始化其父類
虛擬機啓動時要執行的主類(包含main()方法的類).net
當我把類加載和初始化分開來看時,我發現不管是餓漢模式仍是懶漢模式都是在調用getInstance方法時,纔會將構造器裏的話打印出來,這樣看是否都是懶漢模式呢?如今看起來實例都是在類初始化的時候建立的。留有疑問。
public static void main(String[] args) { String a = Singleton.NAME; String b = InnerSingleton.NAME; }
可使用這種方法觸發類的裝載,在這裏類的裝載=加載+初始化。使用上述方法的結果以下圖所示:
code
而若是隻裝載類,利用classloader,兩種方法是都不會打印出來的。但是若是利用classforname方法,是和上述截圖的結果一致的。這是由於,classforname除了會將類加載還會對static變量進行初始化,參考 https://blog.csdn.net/qq_27093465/article/details/52262340htm