原型模式和java拷貝

原型模式

定義

指原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。java

特色

不須要知道建立的細節,不調用構造函數json

類型

建立型bash

適用場景
  1. 類初始化消耗較多資源
  2. new產生的一個對象須要很是繁瑣的過程(數據準備、訪問權限等)
  3. 構造函數比較複雜
  4. 循環體中生產大量對象時
優勢
  1. 原型模式性能比直接new 一個對象性能高
  2. 簡化建立過程
缺點
  1. 必須配備克隆方法
  2. 對克隆複雜對象或克隆出的對象進行復雜改造時,容易引入風險。
  3. 深拷貝、淺拷貝必須引用得當

下面看代碼,寫代碼以前咱們先假設一個業務場景,假設咱們如今要發一個構建一個郵件對象給別人發送郵件告訴別人中獎了,這個對象構建起來很是麻煩,固然咱們的代碼不必定會構建很是複雜的對象。ide

郵件工具類函數

public class MailUtil {
    public static void sendMail(Mail mail){
        String outputContent = "向{0}同窗,郵件地址:{1},郵件內容:{2}發送郵件成功";
        System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
    }
    public static void saveOriginMailRecord(Mail mail){
        System.out.println("存儲originMail記錄,originMail:"+mail.getContent());
    }
}
複製代碼

郵件實體類工具

public class Mail implements Cloneable{
    private String name;
    private String emailAddress;
    private String content;
    public Mail(){
        System.out.println("Mail Class Constructor");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Mail{" +
                "name='" + name + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                ", content='" + content + '\'' +
                '}'+super.toString();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("clone mail object");
        return super.clone();
    }
}
複製代碼

這個實體類注意要實現Cloneable接口,而後重寫Object的clone方法,直接返回父類的的clone方法就能夠了,咱們在裏面添加了一個輸入方便等會兒咱們測試。下面看測試類。性能

測試類測試

public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Mail mail = new Mail();
        mail.setContent("初始化模板");
        System.out.println("初始化mail:"+mail);
        for(int i = 0;i < 10;i++){
            Mail mailTemp = (Mail) mail.clone();
            mailTemp.setName("姓名"+i);
            mailTemp.setEmailAddress("姓名"+i+"@redstar.com");
            mailTemp.setContent("恭喜您,你中獎了");
            MailUtil.sendMail(mailTemp);
            System.out.println("克隆的mailTemp:"+mailTemp);
        }
        MailUtil.saveOriginMailRecord(mail);
    }
}
複製代碼

而後咱們看看測試類運行結果。this

克隆的mailTemp:Mail{name='姓名4', emailAddress='姓名4@redstar.com', content='恭喜您,你中獎了'}com.design.pattern.creational.prototype.Mail@312b1dae
clone mail object
向姓名5同窗,郵件地址:姓名5@redstar.com,郵件內容:恭喜您,你中獎了發送郵件成功
克隆的mailTemp:Mail{name='姓名5', emailAddress='姓名5@redstar.com', content='恭喜您,你中獎了'}com.design.pattern.creational.prototype.Mail@7530d0a
clone mail object
向姓名6同窗,郵件地址:姓名6@redstar.com,郵件內容:恭喜您,你中獎了發送郵件成功
克隆的mailTemp:Mail{name='姓名6', emailAddress='姓名6@redstar.com', content='恭喜您,你中獎了'}com.design.pattern.creational.prototype.Mail@27bc2616
clone mail object
向姓名7同窗,郵件地址:姓名7@redstar.com,郵件內容:恭喜您,你中獎了發送郵件成功
克隆的mailTemp:Mail{name='姓名7', emailAddress='姓名7@redstar.com', content='恭喜您,你中獎了'}com.design.pattern.creational.prototype.Mail@3941a79c
clone mail object
向姓名8同窗,郵件地址:姓名8@redstar.com,郵件內容:恭喜您,你中獎了發送郵件成功
克隆的mailTemp:Mail{name='姓名8', emailAddress='姓名8@redstar.com', content='恭喜您,你中獎了'}com.design.pattern.creational.prototype.Mail@506e1b77
clone mail object
向姓名9同窗,郵件地址:姓名9@redstar.com,郵件內容:恭喜您,你中獎了發送郵件成功
克隆的mailTemp:Mail{name='姓名9', emailAddress='姓名9@redstar.com', content='恭喜您,你中獎了'}com.design.pattern.creational.prototype.Mail@4fca772d
存儲originMail記錄,originMail:初始化模板
複製代碼

注意看打印結果的內存地址,他們打印的地址是不同的,他們不是同一個對象。使用clone方式操做的是內存中的二進制文件,比直接new一個對象性能好的多。spa

深克隆和淺克隆

既然講到了clone那咱們就來講說深克隆和淺克隆吧。關於這個咱們看一段代碼。

public class Pig implements Cloneable{
    private String name;
    private Date birthday;

    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

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

    @Override
    public String toString() {
        return "Pig{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}'+super.toString();
    }
}
複製代碼

這段代碼咱們定義了一個pig小豬佩奇類,有兩個屬性名字和生日,咱們寫一下他的測試類。

public class PigTest {
    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Date birthday = new Date(0L);
        Pig pig1 = new Pig("佩奇",birthday);
        Pig pig2 = (Pig) pig1.clone();
        System.out.println(pig1);
        System.out.println(pig2);
    }
}
複製代碼

咱們運行一下看看結果

Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@312b1dae
複製代碼

注意看內存地址,好像是符合咱們的預期,內存地址是不同的,那咱們修改一下,這個生日屬性看看會發生什麼

public class PigTest {
    public static void main(String[] args) throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Date birthday = new Date(0L);
        Pig pig1 = new Pig("佩奇",birthday);
        Pig pig2 = (Pig) pig1.clone();
        System.out.println(pig1);
        System.out.println(pig2);
        pig1.getBirthday().setTime(666666666666L);
        System.out.println(pig1);
        System.out.println(pig2);
    }
}
複製代碼

運行結果:

Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@312b1dae
Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.design.pattern.creational.prototype.clone.Pig@312b1dae
複製代碼

仔細看雖然他們的內存地址不同可是在修改了其中一個生日時發現兩個都改變了。這是由於咱們的這種方式實現的是一種淺拷貝,生日使用的是date類型是一個引用類型,這兩個對象引用的都是同一個地址。爲了獲得咱們期待的結果咱們就要修改成深拷貝的方式。

public class Pig implements Cloneable{
    private String name;
    private Date birthday;

    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Pig pig = (Pig)super.clone();

        //深克隆
        pig.birthday = (Date) pig.birthday.clone();
        return pig;
    }

    @Override
    public String toString() {
        return "Pig{" +
                "name='" + name + '\'' +
                ", birthday=" + birthday +
                '}'+super.toString();
    }
}

複製代碼

此時咱們再看看測試類的運行結果:

Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com..design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@312b1dae
Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}com.design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.design.pattern.creational.prototype.clone.Pig@312b1dae
複製代碼

此時結果就是咱們想要的了。對於深拷貝還有一種方式就是經過json序列化一次成json再講json轉成對象,這樣也能夠實現深拷貝,可是咱們說使用拷貝的目的是拷貝是直接修改二進制文件效率高,至於使用轉json的方式效率怎麼樣我不太清楚,歡迎小夥伴們本身試驗一下把結果和我交流一下。今天的原型型模式就到這裏。

相關文章
相關標籤/搜索