java實現單例模式
單例模式是一種常用的設計模式,是23中設計模式中的一種。
設計模式:設計模式是一種思想,是一種編程思想,是前人經驗的累積。
單例模式概念:在做某個項目中,創建了一個類,那么這個類只能生成一個對象。
單例模式有兩種實現方式分別:餓漢式和懶漢式
一.餓漢式
餓漢式:顧名思義,可以理解成一個餓漢,只要一加載類的時候就會創建了對象,因為用了static修飾,static修飾的成員隨着類的加載而加載的
實現步驟:1:構造函數私有化
2:提供一個靜態對象屬性用來接收對象
3:創建一個靜態方法用來獲取對象實例
具體代碼如下:
//那么怎么讓這個類在整個項目中生成一個對象呢?使用單例模式 //單例模式第一種:餓漢式 //弊端:占用內存,如果我不想隨着類的加載而加載,想延遲加載創建對象?需要用到懶漢式 //為什么叫餓漢式呢?一加載類的時候就創建了對象,因為用了static修飾,static修飾的成員隨着類的加載而加載 //1.第一步:構造方法需要私有化 //2.第二步:提供一個靜態對象屬性用來接收對象 //3.第三步:提供一個靜態方法用來獲取對象 //隨着類的加載就馬上創建了對象,很嘰餓 public static Single s=new Single(); //私有的只能在本類中使用 private Single(){} public static Single getSingle(){ return s; }
二.懶漢式
懶漢式:比較懶的加載,不會隨着類的加載而加載,而是在你調用創建對象實例的方法時才會創建對想
實現步驟:
第一步:構造方法需要私有化
第二步:提供一個靜態屬性
第三步:提供一個方法用來創建對象
代碼實現如 :
//懶漢式:顧名思義,這個是比較的懶的,在我們加載類的時候沒有創建對象,而是在你調用了方法的時候才創建 //第一步:構造方法需要私有化 //第二步:提供一個靜態屬性 //第三步:提供一個方法用來創建對象 public static LazySingle lazySingle; private LazySingle(){} public static LazySingle getLazySingle(){ //這里面需要創建對象 //怎么解決?也就是說如何避免兩個不同的線程創建不同的對象? //判斷對象是否為空,為空進去創建,不為空就不能夠創建 //需要使用同步把生成的對象的相關代碼鎖起來(同步代碼塊或者同步方法都可以) // synchronized(鎖對象) {} LazySingle.class字節碼對象 //LazySingle加載到內存中去會編譯成LazySingle.class文件, // 到JVM內存中,首先會加載到方法區, // 方法區中可能不止一個LazySingle.class文件,可能會有Dag.class,Cat.class... // 這樣方法區中會存在很多的字節碼文件,用面向對象的思維編寫它,專門用一個類稱呼它Class類 //那么每一個字節碼文件可以看成是一個字節碼文件對象 // Class<LazySingle> lazySingleClass = LazySingle.class;//Class類,Class類中的 LazySingle.class對象 //還沒有很完善,因為使用同步代碼塊會存在開鎖和關鎖的一個步驟,會降低執行效率,損耗時間 //而且不同的線程去訪問它的時候都要執行同步代碼塊,會降低執行效率,消耗時間 //因為使用同步代碼塊會降低執行效率,消耗時間,為了避免減少使用同步代碼塊的一個次數 //需要判斷如果對象為空的時候才進入同步代碼塊 //假如沒有創建對象之前,兩個不同的線程同時進入,那么所創建的對象也不一樣 // 就需要用同步代碼塊把生成對象的相關代碼鎖起來 // 鎖對象使用類的字節碼對象 因為使用同步代碼塊會消耗時間 // 為了避免減少使用同步代碼塊的次數,需要判斷對象為空的時候,才進入同步代碼塊 //這里是進行了雙重檢查 兩個if判斷 if(lazySingle==null) {//如果不等於就不要創建,等於null創建 有利於提升執行效率 synchronized (LazySingle.class) {//鎖對象使用字節碼對象 //如何避免兩個線程創建創建不同的對象 //判斷對象是否為空 //判斷對象是否為空,為空進去創建,不為空就不能夠創建 if (lazySingle == null) { lazySingle = new LazySingle(); } } } return lazySingle; } public void eat(){ System.out.println("大口吃飯"); }
三.創建測試類進行測試
代碼如下:
//餓漢式 Single single = Single.getSingle();//創建兩個對象 Single single1 = single.getSingle(); //如果返回true代表兩個對象是同一個對象,反之則不是 System.out.println(single==single1);//結果為true System.out.println(single); System.out.println(single1); //懶漢式 LazySingle lazySingle = LazySingle.getLazySingle(); LazySingle lazySingle1 = LazySingle.getLazySingle(); //假如沒有創建對象之前,兩個不同的線程同時進入(發生的幾率很小),那么所創建的對象也會不一樣 System.out.println(lazySingle==lazySingle1);//結果為true System.out.println(lazySingle); System.out.println(lazySingle1);
擴展:可以十一下把雙重檢查兩個if判斷去掉則會出現輸出結果flase
不同線程去訪問的時候,那么它們所創建的對象是不一樣的
原因是什么?因為調用這個方法才去創建對象,相當去有一個線程去訪問這個方法
為什么會出現false?不同線程去訪問對象的時候,創建的對象是不一樣的
圖解如下:

怎么解決?也就是說如何避免兩個不同的線程創建不同的對象?加一個if判斷,判斷lazySingle == null是否為空,等於空就進入下面的代碼,反正,則不進入。