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

深拷貝與淺拷貝

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

// 引用類型 sb1調(diào)用自身方法會(huì)影響到sb2,賦值操作就是對(duì)地址的復(fù)制,指向同一個(gè)實(shí)例
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

深拷貝:深拷貝就是希望對(duì)象A和對(duì)象B的操作互不影響。

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

// 對(duì)User的對(duì)象進(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ù)制的對(duì)象
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)嵌套的對(duì)象越來(lái)越多,這種方法顯得繁瑣而且易出錯(cuò)

方法二:使用clone()

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

進(jìn)行拷貝的類需要實(shí)現(xiàn)Cloneable接口,這是個(gè)標(biāo)記接口,沒(méi)有任何方法,實(shí)現(xiàn)這個(gè)接口的類表示調(diào)用clone()合法。不實(shí)現(xiàn)Cloneable調(diào)用clone()會(huì)拋出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ù)制的對(duì)象
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

這依然是淺拷貝,因?yàn)閡ser實(shí)例內(nèi)存區(qū)域的address對(duì)象依然是個(gè)地址,所以需要對(duì)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ù)制的對(duì)象
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)用,那么有沒(méi)有更方便的方法呢。

方法三:序列化

首先是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ù)制的對(duì)象
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ù)制的對(duì)象
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

原型模式

簡(jiǎn)單來(lái)說(shuō),原型模式就是通過(guò)一個(gè)方法獲得一個(gè)實(shí)例的深拷貝,這里的深拷貝是通過(guò)clone(),具體代碼就是上面的代碼,原型模式很簡(jiǎn)單,主要是理解淺拷貝和深拷貝。

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

// todo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容