您好,登錄后才能下訂單哦!
今天小編給大家分享一下Java設計模式之單例和原型實例分析的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
今天老王又來了,還是想買我們的產品,今天老王上老就提出來一個要求,當他購買產品的時候,每次都要從貨架上給他拿相同的一個。
如果用傳統實現方式,當老王拿到產品以后,直接和上一個比對一下就行了,如果不一致老王就還回來。
但通過我們查閱軟件的七大設計原則 ,這很明顯違反了依賴倒置原則,為了避免耦合和讓代碼更易于維護,老王是不能依賴具體產品的。
我們就需要將產品比對在創建產品的時候進行判斷,老王就只管拿。
老王來之前應該還有兩種情況,一種就是老王還沒來,產品就準備好了,也即餓漢式。第二種就是老王什么時候來,什么時候給他準備產品,也即懶漢式。
我們看具體的實現代碼:
懶漢式:
/** * 懶漢式 * @author tcy * @Date 29-07-2022 */ public class LazySingletonProduct { private static volatile LazySingletonProduct instance=null; private LazySingletonProduct(){} public static synchronized LazySingletonProduct getInstance(){ if (instance==null){ instance=new LazySingletonProduct(); } return instance; }
餓漢式:
/** * 餓漢式 * @author tcy * @Date 29-07-2022 */ public class HungrySingletonProduct { private static volatile HungrySingletonProduct instance=new HungrySingletonProduct(); private HungrySingletonProduct(){}; public static synchronized HungrySingletonProduct getInstance(){ if (instance==null){ instance=new HungrySingletonProduct(); } return instance; } }
老王類:
/** * @author tcy * @Date 29-07-2022 */ public class Client { public static void main(String[] args) { HungrySingletonProduct instance1 = HungrySingletonProduct.getInstance(); HungrySingletonProduct instance2 = HungrySingletonProduct.getInstance(); if (instance1==instance2){ System.out.println("我倆一樣..."); }else { System.out.println("我倆不一樣..."); } } }
以上就是單例設計模式中的懶漢式和餓漢式,應該是設計模式中最簡單的一個,理解起來難度也不大。
為了克服老王和他兒子小王一起來拿錯的尷尬,我們在方法上加synchronized鎖,對象引用上加volatile共享變量,但這樣會帶來效率問題,如果不考慮多線程需求,讀者可自行去掉。
老王今天很明顯是找茬,他繼續說,如果我不想要一個了,我要每次買都要不同的,你看著辦。
每次創建產品都要不同的,傳統的方式肯定就是重新new一個對象,但每創建一個對象都是一個復雜的過程,而且這樣還會帶來一定的代碼冗余。
這就需要用到創建型設計模式中的原型模式中的拷貝,其中又分為淺拷貝和深拷貝。
我們先看基本概念。
淺克隆:創建一個新對象,對象種屬性和原來對象的屬性完全相同,對于非基本類型屬性仍指向原有屬性所指向的內存地址
深克隆:創建一個新對象,屬性中引用類型也會被克隆,不再指向原來屬性所指向的內存地址
這段意思也就是,老王購買產品的時候,如果產品都是基本數據類型(byte(位)、short(短整數)、int(整數)、long(長整數)、float(單精度)、double(雙精度)、char(字符)和boolean(布爾值))和String,那么我們就使用淺拷貝。
如果產品包括別的產品(對象)的引用類型就要使用深拷貝。
如果想搞明白,為什么造成深拷貝和淺拷貝這個問題,我們就要重點說說JVM的內存模型。
我們聲明一個基本數據類型的變量a=2,實際上是在棧中直接存儲了一個a=2,當拷貝的時候直接把值拷貝過去,也就是直接有了一份a的副本。
當我們創建一個對象時Student stu=new Student(),實際上對象的值存儲在堆中,在棧中只存放了stu="對象地址",stu指向了堆中的地址,jvm拷貝的時候只復制了棧中的地址,實際上他們堆中的對象還是一個。
我們再來看String類型。String 存在于堆內存、常量池;這種比較特殊, 傳遞是引用地址;由本身的final性, 每次賦值都是一個新的引用地址,原對象的引用和副本的引用互不影響。因此String就和基本數據類型一樣,表現出了"深拷貝"特性。
我們具體看實現代碼:
淺拷貝類:
/** * @author tcy * @Date 29-07-2022 */ public class ShallowProduct implements Cloneable{ private String name; private int num; public void show(){ System.out.println("這是淺產品..."+name+"數量:"+num); } public String getName() { return name; } public ShallowProduct setName(String name) { this.name = name; return this; } public int getNum() { return num; } public ShallowProduct setNum(int num) { this.num = num; return this; } @Override public ShallowProduct clone() throws CloneNotSupportedException { return (ShallowProduct) super.clone(); } }
如果需要哪個對象淺拷貝,需要該對象實現Cloneable接口,并重寫clone()方法。
public void shallowTest()throws CloneNotSupportedException{ ShallowProduct product1=new ShallowProduct(); ShallowProduct product2 = product1.clone(); product1.setName("老王"); product2.setName("老李"); product1.setNum(1); product2.setNum(2); product1.show(); product2.show(); }
調用時輸出的對象中的值直接就是兩個不同的對象,實現了對象的淺拷貝。
如果該對象中包括引用類型呢?那怎么實現呢。
其實原理上也是很簡單的,只需要將非基本數據類型也像淺拷貝那樣操做就行了,然后在當前clone()方法中,調用非基本數據類型的clone()方法
深拷貝引用類:
/** * @author tcy * @Date 29-07-2022 */ public class Child implements Cloneable{ private String childName; public String getChildName() { return childName; } public Child setChildName(String childName) { this.childName = childName; return this; } @Override protected Child clone() throws CloneNotSupportedException { return (Child) super.clone(); } }
深拷貝類:
/** * @author tcy * @Date 29-07-2022 */ public class DeepProduct implements Cloneable{ private String name; private Integer num; private Child child; public String getName() { return name; } public DeepProduct setName(String name) { this.name = name; return this; } public Integer getNum() { return num; } public DeepProduct setNum(Integer num) { this.num = num; return this; } public void show(){ System.out.println("這是深產品..."+name+"數量:"+num+"包括child:"+child.getChildName()); } @Override public DeepProduct clone() throws CloneNotSupportedException { DeepProduct clone = (DeepProduct) super.clone(); clone.child=child.clone(); return clone; } public Child getChild() { return child; } public DeepProduct setChild(Child child) { this.child = child; return this; } }
我們測試一下對象中的值是否發生了改變。
public void deepTest() throws CloneNotSupportedException { DeepProduct product1=new DeepProduct(); Child child=new Child(); child.setChildName("老王child"); product1.setName("老王"); product1.setNum(1); product1.setChild(child); //-------------- DeepProduct product2=product1.clone(); product2.setName("老李"); product2.setNum(2); product2.getChild().setChildName("老李child"); product1.show(); product2.show(); }
老李、老王都正確的輸出了,說明實現沒有問題。
這樣就符合了老王的要求。
既然說到了jvm的內存模型,就有必要說一下java中的值傳遞和引用傳遞。
實際上java應該就是值傳遞,在調用方法的時候,如果參數是基本數據類型,那么傳遞的就是副本,我們在方法中無論怎么給他賦值,他原本的值都不會有變化。
在調用方法的時候,如果參數是引用數據類型,那么傳遞的就是這個對象的地址,我們在方法中修改這個對象都會影響他原本的對象。
造成這個現象的原因其實是和淺拷貝、深拷貝的原理是一樣的,都是棧、堆內存的結構導致的。
老王看他的要求都滿足了,最后心滿意足的拿著產品走了。
以上就是“Java設計模式之單例和原型實例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。