【設計模式】Java設計模式 - 原型模式
目錄?? 不斷學習才是王道
?? 繼續踏上學習之路,學之分享筆記
?? 總有一天我也能像各位大佬一樣
??原創作品,更多關注我CSDN: 一個有夢有戲的人
??準備將博客園、CSDN一起記錄分享自己的學習心得!!!
??分享學習心得,歡迎指正,大家一起學習成長!
- 【設計模式】Java設計模式 - 原型模式
- 簡介
- 克隆
- ①、古老辦法
- ②、原型模式
- 淺拷貝
- 深拷貝
- 方式一:通過重寫clone()方法
- 方式二:通過物件序列化
簡介
原型模式(Prototype Pattern)是用于創建重復的物件,同時又能保證性能,這種型別的設計模式屬于創建型模式,它提供了一種創建物件的最佳方式,
這種模式是實作了一個原型介面,該介面用于創建當前物件的克隆,當直接創建物件的代價比較大時,則采用這種模式,
克隆
原型模式對某個物件進行克隆,在最原始的古老方法就是通過new出實體,使用與之相同的引數,但是每次創建新的物件時候需要重新獲取一下屬性,效率不佳,
例子如下:
①、古老辦法
本次使用lombok,省的還需要寫get,set,toString;坐標匯入如下:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
物體類,使用lombok的@Data注解
package com.lyd.demo.entity;
import lombok.Data;
/**
* @Author: lyd
* @Description:
* @Date: 2022-08-25
*/
@Data
public class Car {
private String type;
private String color;
public Car(String type, String color) {
this.type = type;
this.color = color;
}
}
使用古老辦法,使用new一個一個賦值,很明顯有些缺點,每次都需要呼叫物件去獲取屬性
package com.lyd.demo.ancient;
import com.lyd.demo.entity.Car;
/**
* @Author: lyd
* @Description:
* @Date: 2022-08-25
*/
public class Test {
public static void main(String[] args) {
Car car = new Car("奔馳", "black");
// 復制更多物件
Car c1 = new Car(car.getType(), car.getColor());
Car c2 = new Car(car.getType(), car.getColor());
Car c3 = new Car(car.getType(), car.getColor());
System.out.println("car: " + car + " hashcode: " + car.hashCode());
System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode());
System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode());
System.out.println("c3: " + c3 + " hashcode: " + c3.hashCode());
}
}
運行結果:可見雖然是復制成功了,但是效率明顯很低,

②、原型模式
用原型實體指定創建物件的種類,通過拷貝實體創建新的物件,
例子如下代碼:
需要讓car實作Cloneable并且實作clone方法,
package com.lyd.demo.entity;
import lombok.Data;
/**
* @Author: lyd
* @Description:
* @Date: 2022-08-25
*/
@Data
public class Car implements Cloneable {
private String type;
private String color;
public Car(String type, String color) {
this.type = type;
this.color = color;
}
// 克隆實體
@Override
protected Object clone() throws CloneNotSupportedException {
Car car = null;
car = (Car) super.clone();
return car;
}
}
測驗:
package com.lyd.demo.entity;
/**
* @Author: lyd
* @Description:
* @Date: 2022-08-25
*/
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Car car = new Car("紅旗", "red");
Car c1 = (Car) car.clone();
Car c2 = (Car) car.clone();
Car c3 = (Car) car.clone();
System.out.println("car: " + car + " hashcode: " + car.hashCode());
System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode());
System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode());
System.out.println("c3: " + c3 + " hashcode: " + c3.hashCode());
}
}
運行結果:

定義一個原型類,宣告出克隆自己的介面,克隆的時候只要呼叫clone方法就可以實作物件克隆,
淺拷貝
對于資料型別是基本資料型別的成員變數,淺拷貝會直接進行值傳遞,也就是將該屬性復制一份給新的物件,因為是兩份不同的資料,所以對其中一的物件的成員變數值進行修改,不會影響另一個物件拷貝得到的資料,
對于資料型別是參考型別的成員變數,比如說成員變數是某個陣列,某個類的物件等,那么淺拷貝會進行參考傳遞,也就是只是將該成員變數的參考指(記憶體地址)復制一份給新的物件,因為實際上兩個物件的該成員變數都指向同一個實體,在這種情況下,在一個物件中修改該成員變數會影響到另一個物件的該成員變數值,

在Car類中加一個屬性
public Car NewEnergy;
撰寫測驗淺拷貝類
package com.lyd.demo.entity;
/**
* @Author: lyd
* @Description: 淺拷貝
* @Date: 2022-08-25
*/
public class ShallowCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Car car = new Car("紅旗", "red");
car.NewEnergy = new Car("奔馳", "blue");
// 克隆
Car c1 = (Car) car.clone();
Car c2 = (Car) car.clone();
Car c3 = (Car) car.clone();
System.out.println("car: " + car + " hashcode: " + car.hashCode() + " NewEnergy: " + car.NewEnergy + " hashcode: " + car.NewEnergy.hashCode());
System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode() + " NewEnergy: " + c1.NewEnergy + " hashcode: " + c1.NewEnergy.hashCode());
System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode() + " NewEnergy: " + c2.NewEnergy + " hashcode: " + c2.NewEnergy.hashCode());
System.out.println("c3: " + c3 + " hashcode: " + c3.hashCode() + " NewEnergy: " + c3.NewEnergy + " hashcode: " + c3.NewEnergy.hashCode());
}
}
測驗結果
可見,如果是基本資料型別,那就會傳遞值,將值賦給新的物件;對于物件,則只是進行參考傳遞,這種就是淺拷貝,這樣會導致只要修改其中一個變數,就會導致所有都修改,例如 c2.NewEnergy.setColor("red"); 之后在列印一次資料,

深拷貝
深拷貝復制變數值,對于參考資料,則遞回至基本型別后,再復制,深拷貝后的物件與原來的物件是完全隔離的,互不影響,對一個物件的修改并不會影響另一個物件,深拷貝相比于淺拷貝速度較慢并且花銷較大,
重新定義兩個物體類,其中一個只有基本資料型別,另一個包含參考資料型別
方式一:通過重寫clone()方法
Car: 在克隆的時候直接呼叫super.clone()來完成對基本資料型別的克隆,通過參考資料類的clone方法在進行型別強制裝換即可,
package com.lyd.demo.deep;
import lombok.Data;
import java.io.Serializable;
/**
* @Author: lyd
* @Description: 汽車類
* @Date: 2022-08-25
*/
@Data
public class Car implements Cloneable, Serializable {
private String type;
private String color;
public NewEnergy newEnergy; // 參考資料型別
public Car(String type, String color) {
this.type = type;
this.color = color;
}
// 克隆實體
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = null;
// 完成基本資料的裝換
object = super.clone();
// 對參考資料型別的屬性,單獨處理
Car car = (Car) object;
car.newEnergy = (NewEnergy) newEnergy.clone();
return car;
}
}
NewEnergy:
package com.lyd.demo.deep;
import lombok.Data;
import java.io.Serializable;
/**
* @Author: lyd
* @Description: 新能源類
* @Date: 2022-08-25
*/
@Data
public class NewEnergy implements Serializable, Cloneable {
private String type;
private String color;
public NewEnergy(String type, String color) {
this.type = type;
this.color = color;
}
/**
* 該類都是基本資料型別,直接回傳即可
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
測驗:
package com.lyd.demo.deep;
/**
* @Author: lyd
* @Description: 深拷貝
* @Date: 2022-08-25
*/
public class DeepCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Car car = new Car("路虎", "green");
car.newEnergy = new NewEnergy("路虎新能源", "blue");
Car c1 = (Car) car.clone();
Car c2 = (Car) car.clone();
System.out.println("car: " + car + " hashcode: " + car.hashCode() + " NewEnergy: " + car.newEnergy + " hashcode: " + car.newEnergy.hashCode());
System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode() + " NewEnergy: " + c1.newEnergy + " hashcode: " + c1.newEnergy.hashCode());
System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode() + " NewEnergy: " + c2.newEnergy + " hashcode: " + c2.newEnergy.hashCode());
System.out.println("==============================================================================================================================");
c1.newEnergy.setColor("red"); // 修改
System.out.println("car: " + car + " hashcode: " + car.hashCode() + " NewEnergy: " + car.newEnergy + " hashcode: " + car.newEnergy.hashCode());
System.out.println("c1: " + c1 + " hashcode: " + c1.hashCode() + " NewEnergy: " + c1.newEnergy + " hashcode: " + c1.newEnergy.hashCode());
System.out.println("c2: " + c2 + " hashcode: " + c2.hashCode() + " NewEnergy: " + c2.newEnergy + " hashcode: " + c2.newEnergy.hashCode());
}
}
運行結果:
可見,不但完成了把參考物件進行拷貝,修改c1的屬性時,不會影響其他的物件,這就是深拷貝
關于深拷貝、淺拷貝查看參考資料:CSDNhttps://blog.csdn.net/qq_34748010/article/details/124917704
方式二:通過物件序列化
通過物件流的方式將物件進行序列化之后在進行反序列化完成深拷貝,這樣的效果會更好,
只要在Car類中添加一個deepClone方法
// 方式二:實作序列化
public Object deepClone() {
// 創建流物件
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); // 把當前物件以物件流的方式輸出
//反序列
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
Car car = (Car) ois.readObject();
return car;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
// 關閉流
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
測驗:只要呼叫deepClone方法就可以
Car c1 = (Car) car.deepClone();
Car c2 = (Car) car.deepClone();
最后實驗結果:

??今天的內容不少!創作不易,如有錯誤請指正,感謝觀看!記得一鍵三連哦!??
??德德小建議:
理解設計模式不是一件簡單的事情,需要不斷的學習和動手去練習,才能理解,只有掌握好設計模式,才能夠真正的理解SpringAOP和Mybatis的底層原理,各位讀者可以和我一樣,動手敲一敲代碼,甚至用不同的例子來做,通過debug一步一步除錯,還有就是多看看別人的例子,能夠有助于理解!謝謝各位觀看指點!?? ?? ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/505353.html
標籤:設計模式
