当前位置: 首页 > news >正文

Java浅克隆和深克隆

一、何为克隆

在Java的体系中,数据类型分为基本数据类型引用数据类型

基本数据类型包括byte,short,int,long,float,double,boolean,char 8种,其克隆可通过赋值运算实现,比如

int a = 1;
int b = a;

引用类型的克隆的实现方式有以下两种:

1)实现Cloneable接口,重写clone() 方法,修改clone() 方法的修饰符为public。其分为浅克隆深克隆

2)  实现Serializable接口,对实例进行序列化,通过二进制流反序列化。其为真正的克隆

注:若类没实现Cloneable接口,调用对象的clone方法会抛出CloneNotSupportedException异常

二、重写clone实现克隆

克隆就是获得当前对象的副本,其与当前对象是内存地址不同的两个对象。

浅克隆指的是当前对象的实例成员为引用类型时,副本的该实例成员只是复制了其引用值,指向同一个对象。

深克隆则是对该引用指向的对象进行克隆,然后引用指向了该副本,指向不同的对象。

浅克隆导致了一个问题就是,对实例或克隆副本做出的改变会影响彼此,从而导致错误的结果,下面的例子可以说明:

汽车类 Car,包含一个引用类型成员brand:

/**
 * Car类
 * 实现Cloneable接口,重写clone方法
 * @author zhangyj
 *
 */
public class Car implements Cloneable{
    //使用年限
    private int year;
    //品牌
    private Brand brand; 
    
    public int getYear() {
        return year;
    }
    public void setYear(int year) {
        this.year = year;
    }
    public Brand getBrand() {
        return brand;
    }
    public void setBrand(Brand brand) {
        this.brand = brand;
    }
    
    //构造器
    public Car(int year, Brand brand) {
        this.year = year;
        this.brand = brand;
    }
    
    /**
     * 浅克隆,调用父类的clone方法
     */
    @Override
    public Object clone() {
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }
    
    /**
     * 深克隆,其引用类型成员也要调用clone
     * @return
     */
    public Car deepClone() {
        Car car = null;
        try {
            car = (Car)super.clone();
            car.setBrand((Brand)brand.clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return car;
    }
    
    @Override
    public String toString() {
        return "[year=" + year + ", brand=" + brand + "]";
    }
}
/**
 * 品牌类
 * @author zhangyj
 *
 */
class Brand implements Cloneable{
    //名称
    private String name;

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    protected Object clone() {
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }
    
    public Brand(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Brand [name=" + name + "]";
    }
}

测试类 CarCloneTest :

public class CarCloneTest {
    public static void main(String[] args) {
        Brand brand = new Brand("BMW");
        Car car = new Car(5, brand);
        Car carClone;
        carClone = (Car) car.clone();  //(1)
        //carClone = (Car) car.deepClone(); //(2)
        
        System.out.println("******************************************");
        System.out.println("car "+car);
        System.out.println("carClone "+carClone);
        
        System.out.println("******************************************");
        System.out.println("将car年限改为 7,品牌名称改为Banz");
        brand.setName("Banz");
        car.setYear(7);
        
        System.out.println("******************************************");
        System.out.println("car "+car);
        System.out.println("carClone "+carClone);
    }
}

浅克隆测试,运行(1)代码,得到结果如下:

******************************************
car [year=5, brand=Brand [name=BMW]]
carClone [year=5, brand=Brand [name=BMW]]
******************************************
将car年限改为 7,品牌名称改为Banz ****************************************** car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=Banz]]

可见当修改car中brand对象的名称为Banz时,carClone也跟着改变,可见其引用的brand对象为同一个,显然这不是我们想要的,而深克隆就解决了这个问题。

深克隆测试,运行(2)代码,得到结果如下:

******************************************
car [year=5, brand=Brand [name=BMW]]
carClone [year=5, brand=Brand [name=BMW]]
******************************************
将car年限改为 7,品牌名称改为Banz ****************************************** car [year=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]

三、序列化实现深克隆

序列化对象必须实现Serializable接口,使对象可序列化。

public class Car implements Serializable{
    private static final long serialVersionUID = 7883197573658810857L;
    private int year; //使用年限
    private Brand brand; //品牌
    
    public int getYear() {
        return year;
    }
    public void setYear(int year) {
        this.year = year;
    }
    public Brand getBrand() {
        return brand;
    }
    public void setBrand(Brand brand) {
        this.brand = brand;
    }
    
    public Car(int year, Brand brand) {
        this.year = year;
        this.brand = brand;
    }
    
    @Override
    public String toString() {
        return "[year=" + year + ", brand=" + brand + "]";
    }
}
class Brand implements Serializable{
    private static final long serialVersionUID = -6505899377489945908L;
    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Brand [name=" + name + "]";
    }
}

克隆工具类 CloneUtil: 

public final class CloneUtil {
    private CloneUtil() { throw new AssertionError(); }
    
    /**
     * 序列化实现深克隆
     * @param t 泛型对象
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T deepClone(T t) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objOut;
        ObjectInputStream objIn;
        T tClone = null ;
        try {
            objOut = new ObjectOutputStream(out);
             objOut.writeObject(t);  //将对象以二进制流形式写入
            ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
            objIn = new ObjectInputStream(in);
            tClone = (T)objIn.readObject(); //反序列化读取对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tClone;
    }
}

测试类 CarCloneTest: 

public class CarCloneTest {
    public static void main(String[] args) {
        Brand brand = new Brand("BMW");
        Car car = new Car(5, brand);
        Car carClone;
//        carClone = (Car) car.clone();  //(1)
//        carClone = (Car) car.deepClone(); //(2)
        carClone = CloneUtil.deepClone(car);
        
        System.out.println("******************************************");
        System.out.println("car "+car);
        System.out.println("carClone "+carClone);
        
        System.out.println("******************************************");
        System.out.println("将car年限改为 7,品牌名称改为Banz");
        brand.setName("Banz");
        car.setYear(7);
        
        System.out.println("******************************************");
        System.out.println("car "+car);
        System.out.println("carClone "+carClone);
    }
}

运行测试程序,得到结果如下:

******************************************
car [year=5, brand=Brand [name=BMW]]
carClone [year=5, brand=Brand [name=BMW]]
******************************************
将car年限改为 7,品牌名称改为Banz
****************************************** 
car [year
=7, brand=Brand [name=Banz]] carClone [year=5, brand=Brand [name=BMW]]

 可见通过反序列化对象进行克隆也能得到我们想要的结果。

以上!

转载于:https://www.cnblogs.com/zhangyuejia/p/8625698.html

相关文章:

  • C#注册OCX控件
  • Hibernate 一对一关联映射,mappedBy参数解析
  • range.FormulaR1C1属性
  • java学习--基础知识进阶第十天--笔记
  • 七、数据库技术的发展及新技术
  • C语言实现过滤ASCII在0~127范围内的字符,并去除重复的字符
  • JAVA gc垃圾回收机制
  • C#任意格式字符串转化为datetime格式
  • Jquery 遍历数组之$().each方法与$.each()方法介绍
  • kafka集群搭建
  • 《人月神话》读书笔记 第四周
  • py网络爬虫基础练习
  • 解决导入TensorFlow后出现警告的的问题解决:通过降低numpy的版本
  • mongodb的安装和sql操作
  • 面试总结篇(一)
  • Consul Config 使用Git做版本控制的实现
  • django开发-定时任务的使用
  • k个最大的数及变种小结
  • Lsb图片隐写
  • Swift 中的尾递归和蹦床
  • Zepto.js源码学习之二
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 构建二叉树进行数值数组的去重及优化
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 写给高年级小学生看的《Bash 指南》
  • 原生Ajax
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • C# - 为值类型重定义相等性
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • #includecmath
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (2)nginx 安装、启停
  • (3)nginx 配置(nginx.conf)
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)ssm码农论坛 毕业设计 231126
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (四)Linux Shell编程——输入输出重定向
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (一)认识微服务
  • (原)Matlab的svmtrain和svmclassify
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • ****Linux下Mysql的安装和配置
  • .naturalWidth 和naturalHeight属性,
  • .NET CORE 第一节 创建基本的 asp.net core
  • .net core 依赖注入的基本用发
  • .NET delegate 委托 、 Event 事件
  • .net framework profiles /.net framework 配置
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET处理HTTP请求
  • .NET中GET与SET的用法
  • .net中调用windows performance记录性能信息