原型模式和深拷貝,淺拷貝

来源:https://www.cnblogs.com/changming06/archive/2023/09/08/17688657.html
-Advertisement-
Play Games

### 原型模式 #### 案例引入 ##### 克隆羊問題 有一隻羊,姓名為tom,年齡為1,顏色為白色,編寫程式創建和tom羊屬性完全相同的羊。 ##### 傳統方式解決 代碼實現 ```java public class Sheep { private String name; private ...


原型模式

案例引入

克隆羊問題

有一隻羊,姓名為tom,年齡為1,顏色為白色,編寫程式創建和tom羊屬性完全相同的羊。

傳統方式解決

代碼實現

public class Sheep {
    private String name;
    private int age;
    private String color;

    public Sheep() {
    }

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
//測試
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom", 1, "白色");
        Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        /**
        *  public String toString() {//Object類的toString()方法
        *      return getClass().getName() + "@" + Integer.toHexString(hashCode());//會輸出全類名@符還有16進位的hashCode值
        *  }
        */
        System.out.println(sheep);//輸出的對象的hashcode的值不相同,輸出對象時,會預設的調用對象的toString()方法
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);
    }
}
傳統實現方式分析
  • 1.優點是好理解,簡單易操作。
  • 2.缺點進行新對象創建時,總是需要重新獲取原始對象的屬性,如果創建的對象複雜時,效率很低。
  • 3.缺點,總是需要重新初始化對象(new操作),而不是動態的根據已有對象去創建,不靈活。
  • 4.改進思路,Java中Object類是所有類的基類,Object類提供了一個clone()方法,該方法可以將一個Java對象賦值一份,但是需要想使用這個方法的類必須要實現一個Cloneable介面,
    該介面才能複製,且具有複製的能力。

原型模式

基本介紹
  • 1.原型模式(Prototype Pattern)是指,用原型實例創建對象的種類,並且通過拷貝這些原型,創建新的對象。
  • 2.原型模式是一種設計型模式,允許一個對象再創建另一個可對象,無需知道創建的細節。
  • 3.工作原理是,通過將一個原型對象通過clone或者其他自己寫的克隆方法,拷貝自身。
用原型模式實現案例
public class Sheep implements Cloneable{
    private String name;
    private int age;
    private String color;
    private String address = "蒙古羊";
    private Sheep friend;

    public Sheep() {
    }

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Sheep getFriend() {
        return friend;
    }

    public void setFriend(Sheep friend) {
        this.friend = friend;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", address='" + address + '\'' +
                ", friend=" + friend +
                '}';
    }

    @Override
    protected Object clone() {
        Sheep sheep = null;
        try{
            sheep = (Sheep)super.clone();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        return sheep;
    }
}

public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom", 1, "白色");
        sheep.setFriend(new Sheep("jack",1,"黑色"));

        Sheep sheep1 = (Sheep)sheep.clone();
        Sheep sheep2 = (Sheep)sheep.clone();

        System.out.println("sheep1=" + sheep1 + "sheep1.friend\n" + sheep1.getFriend().hashCode());//輸出的hashCode值相同
        System.out.println("sheep2=" + sheep2 + "sheep2.friend\n" + sheep2.getFriend().hashCode());
    }
}

原型模式在Spring框架中的使用

//AbstractBeanFactory類doGetBean()的部分代碼
else if (mbd.isPrototype()) {//判斷當前bean是否是原型類型
   // It's a prototype -> create a new instance.
   Object prototypeInstance = null;
   try {
      beforePrototypeCreation(beanName);
      prototypeInstance = createBean(beanName, mbd, args);
   }
   finally {
      afterPrototypeCreation(beanName);
   }
   bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

深拷貝和淺拷貝

淺拷貝介紹
  • 1.淺拷貝,對於基本數據類型的屬性,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的對象。
  • 2.對於數據類型是引用數據類型的成員變數,比如說成員變數是某個數組,某個類的對象等,淺拷貝會進行引用傳遞,也就是只是將該成員變數的引用值(記憶體地址)複製給新的對象。實際上兩個對象的該成員變數都指向一個實例。在這種情況下,一個對象對於引用類型的屬性的改變,就會影響到另一個對象的引用屬性。
  • 3.前面克隆羊就是淺拷貝。
  • 4.淺拷貝是使用預設的clone方法來實現 sheep = (sheep) super.clone();
深拷貝介紹
  • 1.複製對象的所有屬性,進行拷貝,就是說,基本數據類型拷貝成員變數值,引用數據類型的屬性都申請存儲空間,並複製每個引用類型屬性所引用的對象,進行拷貝。也就是說對象進行深拷貝要對整個對象(包括對象的引用屬性)進行拷貝。
  • 2.深拷貝實現方式,重寫clone()和通過對象序列化和反序列化實現深拷貝(推薦)
    序列化,簡單的說,就是將對象存儲到磁碟。反序列化,將磁碟存儲的對象讀取到記憶體。
    代碼演示
public class Money implements Serializable, Cloneable{
    private int account;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Person implements Serializable, Cloneable {
    private String name;
    private int age;
    private Money money;
    private static final long serialVersionID = 1L;

    public Person(String name, int age, Money money) {
        this.name = name;
        this.age = age;
        this.money = money;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Money getMoney() {
        return money;
    }

    public void setMoney(Money money) {
        this.money = money;
    }

    //1.重寫clone方法完成深拷貝
    //這種方式存在問題,如果一個類中存在引用屬性且沒有為該屬性複製
    //就會導致在進行克隆時會出現NullPointException,可以在clone前進行驗證解決
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = null;
        //1.完成基本數據類型的clone
        person = (Person) super.clone();
        //2.對引用類型的屬性,進行單獨處理
        person.money = (Money) money.clone();
        return person;
    }

    //方式2 通過對象的序列化和反序列化實現
    public Object deepClone() {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        Object object = null;
        try{
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);//當前對象序列化到對象輸出流中
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);//從輸出流中反序列化到輸入流中
            object = ois.readObject();//讀取對象
        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return object;
    }
}
//測試
public class TestDeepClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        Money money = new Money();
        Person person = new Person("wind", 27, null);

        Person clone = (Person) person.clone();
//        Person clone = (Person) person.deepClone();
        System.out.println(person.getMoney().hashCode() + "-" + clone.getMoney().hashCode());
    }
}

註意事項和細節

  • 1.創建新的對象比較複雜,可以利用原型模式簡化對象的創建過程,同時也能提高效率。
  • 2.不用再重新初始化對象,而是動態的根據對象的運行進行創建。
  • 3.如果原始對象發送變化(增加或減少屬性),克隆對象的也不要發送變化,無需修改克隆的代碼。
  • 4.實現深拷貝,需要比較複雜的代碼。
  • 5.缺點,要為需要克隆的類,配一個克隆方法,這對新創建的類不難,但是對已有的類,需要修改源代碼,違反了ocp。
    只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近研發apk校驗服務,很多游戲安裝包兩三個G,如果整個拿去校驗,耗時基本二十多秒,這還僅僅是校驗的時間,如果加上下載的時間,等待時間太長了 網上很多方案嘗試了一下,不太行 1、fast md5 一個第三方庫,csdn有人用過說可以提升40%的速度,然後我去試了一下,本來9秒可以完成的校驗,變成了2 ...
  • 如果你會 Java, 那麼來看一看 Kotlin , 基礎入門。 如果你不理解 Kotlin 的lambda 表達式,那麼來看一看,幫助你真正理解函數類型,lambda 表達式。 ...
  • >我們是[袋鼠雲數棧 UED 團隊](http://ued.dtstack.cn/),致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。 >本文作者:琉易 [liuxianyu.cn](https://link.juejin.cn/?target=ht ...
  • 需求: 設計一個標題,讓中間部分隨著文字而撐大,同時文字漸變,兩邊自適應,這種情況就不能用傳統的背景圖片了,想到可以使用圖片邊框來做 解決思路: 1.需要一個大盒子和三個小盒子 2.大盒子設置display:flex; 左右兩個小盒子分別設置flex-grow; 並設置背景圖片 3.給中間盒子設置邊 ...
  • 此篇文章用於記錄柏成從零開發一個canvas九宮格手勢解鎖器的歷程。我們基於 canvas 實現了一款簡單的九宮格手勢解鎖器,用戶可以通過在九宮格中繪製特定的手勢來解鎖。 ...
  • ##一、定義 **使用原型實例指定待創建對象的類型,並且通過複製這個原型來創建新的對象。原型模式是一種創建型模式。** ##二、描述 **包含以下三個角色:** ![](https://img2023.cnblogs.com/blog/1780813/202305/1780813-202305271 ...
  • 本文給大家介紹了什麼是"編程範式",選擇合適的編程範式可以提高代碼的可讀性、可維護性和可擴展性。 一、 什麼是編程範式? "編程範式"是一種編程思想的總稱,它是指在編寫程式時所採用的基本方法和規範。常見的編程範式有面向對象、函數式、邏輯式等。 選擇合適的編程範式可以提高代碼的可讀性、可維護性和可擴展 ...
  • 淺聊一下SpringMVC的核心組件以及通過源碼瞭解其執行流程 MVC作為WEB項目開發的核心環節,正如三個單詞的分解那樣,Controller(控制器)將View(視圖、用戶客戶端)與Model(javaBean:封裝數據)分開構成了MVC,今天我們淺聊一下SpringMVC的相關組件以及通過源碼... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...