Java實例 Part6:Java中的克隆

来源:https://www.cnblogs.com/hynazwaa/archive/2018/11/04/9906308.html
-Advertisement-
Play Games

Part6:Java中的克隆 @[toc] Example01:Java對象的假克隆 對象的克隆是Java中的一項高級技術,獲得與其相同的對象。   基本數據類型可以使用“=”來進行克隆,此時兩個變數除了相等是沒有任何關係的。而對於引用類型數據不能簡單地使用“=”進行克隆,這與J ...


目錄

Part6:Java中的克隆


@
***

Example01:Java對象的假克隆

  • 對象的克隆是Java中的一項高級技術,獲得與其相同的對象。

  基本數據類型可以使用“=”來進行克隆,此時兩個變數除了相等是沒有任何關係的。而對於引用類型數據不能簡單地使用“=”進行克隆,這與Java的記憶體空間使用有關。   
  Java將記憶體空間分成兩塊,即棧和堆。在棧中保存基本類型和引用變數;在堆中保存對象。對於引用變數而言,使用“=”將修改引用,而不是複製堆中的對象。此時兩個引用變數將指向同一個對象。因此,如果一個變數對其修改則會改變另一個變數。

運行結果:
在這裡插入圖片描述
代碼實現:

public class Employee {
    private String name;
    private int age;

    //省略set()和get()方法
    
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) {
        System.out.println("-----克隆之前:--------");
        Employee employee1 = new Employee();
        employee1.setName("hyn");
        employee1.setAge(20);
        System.out.println("員工1的信息:\n"+employee1);

        System.out.println("-----克隆之後:--------");
        Employee employee2 = employee1;   //將employee1賦值給employee2
        employee2.setName("azw");
        employee2.setAge(21);
        System.out.println("員工1的信息:\n"+employee1);
        System.out.println("員工2的信息:\n"+employee2);
    }
}

Example02:Java對象的淺克隆

  在克隆對象時,如果對象的成員變數是基本數據類型,則使用淺克隆即可完成。如果對象的成員變數包括可變引用類型,則需要深克隆。

運行結果:
在這裡插入圖片描述
代碼實現:

//Address.java
    public class Address {
        private String state;      //所在國家
        private String province;   //所在省
        private String city;       //所在城市
    
        public Address(String state, String province, String city) {
            this.state = state;
            this.province = province;
            this.city = city;
        }
    
        //省略set()和get()方法
        
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("國家:"+state+",");
            sb.append("省:"+province+",");
            sb.append("市:"+city);
            return sb.toString();
        }
    }
//Employee.java
    public class Employee implements Cloneable{
        private String name;
        private int age;
        private Address address;
    
        public Employee(String name, int age, Address address) {
            this.name = name;
            this.age = age;
            this.address = address;
        }
        //省略set()和get()方法
        
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("姓名:"+name+",");
            sb.append("年齡:"+age+",");
            sb.append("\n地址:"+address);
            return sb.toString();
        }
    
        @Override
        public Employee clone() throws CloneNotSupportedException {      //實現淺克隆
            Employee employee = (Employee) super.clone();
            return employee;
        }
    }

測試代碼:

class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        System.out.println("*****克隆之前:******");
        Address address = new Address("中國", "湖北", "武漢");
        Employee employee1 = new Employee("azw", 20, address);
        System.out.println("員工1的信息:\n" + employee1);          //employee1的信息

        System.out.println("*****克隆之後:******");
        Employee employee2 = employee1.clone();  //使用克隆創建Employee2
        employee2.getAddress().setState("中國");   //修改地址
        employee2.getAddress().setProvince("黑龍江");
        employee2.getAddress().setCity("哈爾濱");
        employee2.setName("hyn");
        employee2.setAge(21);
        System.out.println("員工1的信息:\n" + employee1);

        System.out.println("員工2的信息:\n" + employee2);
    }
}
  • 如果引用類型是不可變的,如String類對象,則不必進行深克隆。
    ***

Example03:Java對象的深克隆

  • 如果類的成員變數中包括可變引用類型,則需進行深克隆。

運行結果:
在這裡插入圖片描述
代碼實現:

//Address.java
public class Address implements Cloneable{
    private String state;      //所在國家
    private String province;   //所在省
    private String city;       //所在城市

    public Address(String state, String province, String city) {
        this.state = state;
        this.province = province;
        this.city = city;
    }

    //省略set()和get()方法
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("國家:"+state+",");
        sb.append("省:"+province+",");
        sb.append("市:"+city);
        return sb.toString();
    }
    //---------------------------
    @Override
    public Address clone() throws CloneNotSupportedException { 
 //Address類中的域不是基本類型就是不可變類型,所以可以直接使用淺克隆
        Address address = (Address) super.clone();
        return address;
    }
    //---------------------------
}

//Employee.java
public class Employee implements Cloneable{
    private String name;
    private int age;
    private Address address;

    public Employee(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    //省略set()和get()方法
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("姓名:"+name+",");
        sb.append("年齡:"+age+",");
        sb.append("\n地址:"+address);
        return sb.toString();
    }

    @Override
    public Employee clone() throws CloneNotSupportedException {      //實現深克隆
        Employee employee = (Employee) super.clone();
        //---------------------------------
        employee.address = address.clone();
        //---------------------------------
        return employee;
    }
}

//測試代碼同Example02測試代碼.
  • 要點:通常情況下,需要用到克隆對象時都需要使用深克隆。
    ***

Example04:序列化與對象克隆

  如果類的成員變數比較複雜,例如使用了多個可變的引用類型,使用clone()方法是非常麻煩的,所以可以考慮序列化的方式完成克隆。
運行結果:
在這裡插入圖片描述
代碼實現:

import java.io.Serializable;

public class Employee implements Serializable {
    //同Example04中Employee.java的代碼
}

public class Address implements Serializable {
    //同Example04中Assress.java的代碼
}

測試代碼:

class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println("*****序列化之前:******");
        Address address = new Address("中國", "湖北", "武漢");
        Employee employee1 = new Employee("azw", 20, address);
        System.out.println("員工1的信息:\n" + employee1);          //employee1的信息

        System.out.println("*****序列化之後:******");
        Employee employee2 = null;

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("E:\\employee.txt"));
        out.writeObject(employee1);    //將對象寫入到本地文件中

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("E:\\employee.txt"));
        employee2 = (Employee)in.readObject();   //從本地文件中讀取對象

        if (employee2 != null) {
            employee2.getAddress().setState("中國");   //修改地址
            employee2.getAddress().setProvince("黑龍江");
            employee2.getAddress().setCity("哈爾濱");
            employee2.setName("hyn");
            employee2.setAge(21);
            System.out.println("員工1的信息:\n" + employee1);
            System.out.println("員工2的信息:\n" + employee2);
        }
    }
}

要點:進行序列化的類需要實現Serializable介面,該介面中並沒有定義任何方法,是一個標識介面。如果類中有可變的引用類型成員變數,則該變數需要實現Serializable介面。本實例採用將對象寫入本地文件的方式完成序列化。
***

Example05:深克隆和序列化的效率比較

  • 通過使用這兩種方式克隆100000個對象,並輸出花費的時間來比較這兩種方法的效率。

運行結果:
在這裡插入圖片描述
代碼實現:

import java.io.Serializable;

public class Employee implements Cloneable,Serializable {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("姓名:"+name+",");
        sb.append("年齡:"+age+",");
        return sb.toString();
    }

    @Override
    public Employee clone() throws CloneNotSupportedException {    //使用父類的clone()方法實現深克隆
        Employee employee = (Employee) super.clone();
        return employee;
    }
}
測試代碼:
import java.io.*;
import java.util.ArrayList;
import java.util.List;

class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, CloneNotSupportedException {
        List<Employee> employees = new ArrayList<Employee>();   //創建列表保存對象
        Employee employee = new Employee("azw", 20);   //創建對象
        long currentTime = System.currentTimeMillis();   //獲得當前系統時間
        //使用克隆方式獲得對象
        for (int i = 0;i<100000;i++){
            employees.add(employee.clone());
        }
        System.out.println("克隆花費的時間:"+(System.currentTimeMillis()-currentTime)+"毫秒");

        currentTime = System.currentTimeMillis();   //獲得當前系統時間
        for (int i = 0;i<100000;i++){
            ByteArrayOutputStream bout = new ByteArrayOutputStream();   //創建位元組數組輸出流
            ObjectOutputStream out = new ObjectOutputStream(bout);  //創建對象輸出流
            out.writeObject(employee);   //將對象寫入到輸出流中
            //獲得位元組輸出流內容
            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
            ObjectInputStream in = new ObjectInputStream(bin);   //創建對象輸入流
            employees.add((Employee) in.readObject());   //讀取對象
        }
        System.out.println("序列化花費的時間:"+(System.currentTimeMillis()-currentTime)+"毫秒");
    }
}

要點:使用ByteArrayOutputStream和ByteArrayInputStream可以將對象保存在記憶體中,這樣就不必產生一個本地文件來完成序列化的功能。
***

假克隆、淺克隆和深克隆的應用範圍

假克隆 基本數據類型
淺克隆 基本數據類型、不可變引用類型
深克隆 可變引用類型


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

-Advertisement-
Play Games
更多相關文章
  • 前言 開心一刻 小白問小明:“你前面有一個5米深的坑,裡面沒有水,如果你跳進去後該怎樣出來了?”小明:“躺著出來唄,還能怎麼出來?”小白:“為什麼躺著出來?”小明:“5米深的坑,還沒有水,跳下去不死就很幸運了,殘是肯定會殘的,不躺著出來,那能怎麼出來?”小白:“假設沒死也沒殘呢?”小明:“你當我超人 ...
  • 首先是恭喜IG獲得S8全球總決賽冠軍,IG牛逼。但咱是一介草民,狂歡後,還是得老老實實的開始敲代碼。 最近做了一個給底層固件壓縮加密的工具,是使用C 做的,已經提交出去可以正常使用的。既然在學習Go語言, 那我很慣性的想到,能不能使用Go來做這塊,咱界面還是使用winform,壓縮這一塊可以使用Go ...
  • 基本類型直接存儲在堆棧中 基本類型所具有的包裝容器,使得可以在堆中創建一個非基本對象,用來表示對應的基本類型 ...
  • 日誌功能的實現 Python 自身提供了一個用於記錄日誌的標準庫模塊:logging。 logging 模塊 logging 模塊定義的函數和類為應用程式和庫的開發實現了一個靈活的事件日誌系統 logging 模塊是 Python 的一個標準庫模塊,由標準庫模塊提供日誌記錄 API 的關鍵好處是所有 ...
  • 2018-11-04 23:40:00 開始寫 要配置3個環境變數: 1.JAVA_HOME:用來指定JDK的安裝路徑 2.Path:用來使系統能夠在任何路徑下都可以識別java命令 3.CLASSPATH:用來載入Java類庫的路徑 謝謝、Thank you、Salamat Do(撒拉瑪特朵)、あ ...
  • 學習引薦地址:https://www.cnblogs.com/xdp-gacl/p/3946207.html 一、JDBC的相關概念介紹 1.1 資料庫驅動 其實就好比我們平時使用到的獨立音效卡、網卡之類的外部設備,新插到電腦上並不能正常使用,必須要安裝與之對應的驅動程式方可正常運行。同樣的道理,我們 ...
  • 2018-11-04 23:04:03開始寫 使用List<JavaBean> 謝謝、Thank you、Salamat Do(撒拉瑪特朵)、あリがCám o*n(嘉蒙)とゥ(阿裡嘎都)、감사합니다 (勘三哈咪瘩)、terima Kasih(得力馬卡系)、kob-khun(寇布庫恩)、dhanyav ...
  • 有了STL,不必再從頭寫大多的標準數據結構和演算法,並且可獲得非常高的性能。(如果沒有氧氣,最好不要用vector,deque,set,multiset,map,string)。 廢話不多說,讓我們一起看看STL到底有多好用。 1.vector 可變長度的數組。(可節省空間) 常用操作: 上面的操作時 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...