JAVA設(shè)計模式:原型模式與拷貝

深拷貝與淺拷貝

淺拷貝:對象A進(jìn)行賦值操作得到對象B蟆盹,這就是淺拷貝日缨,修改對象A的屬性會影響到B的屬性

// 引用類型 sb1調(diào)用自身方法會影響到sb2掖看,賦值操作就是對地址的復(fù)制,指向同一個實例
StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = sb1;
sb1.append(" world");
System.out.println(sb1.toString());  // hello world
System.out.println(sb2.toString());  // hello world

深拷貝:深拷貝就是希望對象A和對象B的操作互不影響毅待。

如何實現(xiàn)深拷貝

// 對User的對象進(jìn)行深拷貝
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String name;
    private Address address;

}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Address {
    String province;
    String city;
}
方法一:使用new
// 被復(fù)制的對象
Address address = new Address("hebei", "zhangjiakou");
User user = new User("zhang", address);
// 使用 new 深拷貝
Address addressCopy = new Address(address.getProvince(), address.getCity());
User userCopy = new User(user.getName(), addressCopy);

當(dāng)嵌套的對象越來越多吱涉,這種方法顯得繁瑣而且易出錯

方法二:使用clone()

既然是復(fù)制外里,那么可以把User實例所在的內(nèi)存區(qū)域拷貝一份盅蝗,然后用新引用指向新區(qū)域,事實上Java也提供了這樣的操作芙委,即 Object.clone()

進(jìn)行拷貝的類需要實現(xiàn)Cloneable接口灌侣,這是個標(biāo)記接口裂问,沒有任何方法堪簿,實現(xiàn)這個接口的類表示調(diào)用clone()合法戴甩。不實現(xiàn)Cloneable調(diào)用clone()會拋出CloneNotSupportedException

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Cloneable{

    private String name;
    private Address address;

    @Override
    protected User clone() throws CloneNotSupportedException {
        return (User) super.clone();
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Address {
    String province;
    String city;
}
// 被復(fù)制的對象
Address address = new Address("hebei", "zhangjiakou");
User user = new User("zhang", address);
// 使用 clone() 深拷貝
User userCopy = user.clone();
// 檢查
user.getAddress().setCity("handan");
System.out.println(userCopy.getAddress().getCity()); // handan

這依然是淺拷貝甜孤,因為user實例內(nèi)存區(qū)域的address對象依然是個地址缴川,所以需要對address進(jìn)行拷貝把夸。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Cloneable {

    private String name;
    private Address address;
    
    // change
    @Override
    protected User clone() throws CloneNotSupportedException {
        User user = (User) super.clone();
        Address address = user.getAddress().clone();
        user.setAddress(address);
        return user;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Address implements Cloneable {
    String province;
    String city;
    
    // change
    @Override
    protected Address clone() throws CloneNotSupportedException {
        return (Address) super.clone();
    }
}
// 被復(fù)制的對象
Address address = new Address("hebei", "zhangjiakou");
User user = new User("zhang", address);
// 使用 clone() 深拷貝
User userCopy = user.clone();
// 檢查
user.getAddress().setCity("handan");
System.out.println(userCopy.getAddress().getCity()); // zhangjiakou

這樣在調(diào)用上比new優(yōu)雅許多铭污,但在clone()里面也需要注意嵌套調(diào)用膀篮,那么有沒有更方便的方法呢誓竿。

方法三:序列化

首先是JAVA自帶的序列化功能

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

    private String name;
    private Address address;
    // change
    public User deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (User) ois.readObject();
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Address implements Serializable {
    String province;
    String city;
}
// 被復(fù)制的對象
Address address = new Address("hebei", "zhangjiakou");
User user = new User("zhang", address);
// 使用 Serialize 深拷貝 change
User userCopy = user.deepClone();
// 檢查
user.getAddress().setCity("handan");
System.out.println(userCopy.getAddress().getCity()); // zhangjiakou

使用JSON序列化也可以

// 被復(fù)制的對象
Address address = new Address("hebei", "zhangjiakou");
User user = new User("zhang", address);
// 使用 JSON序列化 深拷貝
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
User userCopy = mapper.readValue(json, User.class);
// 檢查
user.getAddress().setCity("handan");
System.out.println(userCopy.getAddress().getCity()); // zhangjiakou

原型模式

簡單來說,原型模式就是通過一個方法獲得一個實例的深拷貝簸喂,這里的深拷貝是通過clone()喻鳄,具體代碼就是上面的代碼诽表,原型模式很簡單,主要是理解淺拷貝和深拷貝袄简。

原型模式在Spring中的應(yīng)用

// todo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市候址,隨后出現(xiàn)的幾起案子尔破,更是在濱河造成了極大的恐慌轧简,老刑警劉巖践瓷,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炸卑,死亡現(xiàn)場離奇詭異盖文,居然都是意外死亡嘱蛋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洒敏,“玉大人龄恋,你說我怎么就攤上這事⊥┎#” “怎么了篙挽?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長镊靴。 經(jīng)常有香客問我铣卡,道長,這世上最難降的妖魔是什么偏竟? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蝉仇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘害驹。我一直安慰自己,他們只是感情好底洗,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布徐块。 她就那樣靜靜地躺著扳剿,像睡著了一般橙困。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上夏跷,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天猫态,我揣著相機與錄音,去河邊找鬼。 笑死终息,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播摸航,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼擂涛,長吁一口氣:“原來是場噩夢啊……” “哼恢暖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挨队,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤情臭,失蹤者是張志新(化名)和其女友劉穎跷乐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡央勒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年井濒,在試婚紗的時候發(fā)現(xiàn)自己被綠了喻奥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片过牙。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡谦秧,死狀恐怖集歇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站蜻拨,受9級特大地震影響血崭,放射性物質(zhì)發(fā)生泄漏舰讹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦猬腰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罩润。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骨宠。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工桦卒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洞慎,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓花椭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容