IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java随笔-clone -> 正文阅读

[Java知识库]Java随笔-clone

为什么要使用clone

为什么要使用clone,直接new一个对象不就好了吗,实际使用中,直接new一个对象也不好,解决不了"新数据"会影响原始数据的问题。
这里简单创建了一个测试对象WhyClone。

public class WhyClone {
    private String name;

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

然后创建两个WhyClone 对象,并且使用一个对另一个赋值,赋值之后修改被赋值的内容。

    /**
     * 拷贝产生的原因
     */
    static void whyClone(){
        WhyClone wc1 = new WhyClone();
        wc1.setName("wc1");
        WhyClone wc2 = new WhyClone();
        wc2 = wc1;
        // 输出wc1与wc2的地址
        System.out.println(wc1);
        System.out.println(wc2);
        // 输出wc1与wc2的内容
        System.out.println(wc1.getName());
        System.out.println(wc2.getName());
        // 修改wc2的内容
        wc2.setName("wc2");
        // 输出wc1与wc2的地址
        System.out.println(wc1);
        System.out.println(wc2);
        // 输出wc1与wc2的内容
        System.out.println(wc1.getName());
        System.out.println(wc2.getName());
    }

编译结果:

clone.WhyClone@7a07c5b4
clone.WhyClone@7a07c5b4
wc1
wc1
clone.WhyClone@7a07c5b4
clone.WhyClone@7a07c5b4
wc2
wc2

在这里插入图片描述

查看结果发现:wc2被wc1赋值后,wc1和wc2指向同一块地址clone.WhyClone@7a07c5b4,且内容相同,且给wc2赋值字符串”wc2“后,wc1的内容也变成”wc2“。通常开发者需要改变wc2的值且不影响wc1,这就是为什么要使用clone的原因。

clone分为浅拷贝和深拷贝。

浅拷贝

拷贝时,拷贝对象需要实现Cloneable接口,并重写clone() 。

public class ShallowClone implements Cloneable{
    private String name;
    private Inner inner;
    
    public Inner getInner() {
        return inner;
    }
    public void setInner(Inner inner) {
        this.inner = inner;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public static class Inner{
        public Inner(String tag) {
            this.tag = tag;
        }
        public String  tag;
    }
}

测试代码:

    /**
     * 浅拷贝
     */
    static void shallowClone() throws CloneNotSupportedException {
        ShallowClone sc1 = new ShallowClone();
        sc1.setInner(new ShallowClone.Inner("inner"));
        sc1.setName("sc1");
        ShallowClone sc2 = (ShallowClone) sc1.clone();
        // 输出sc1与sc2的地址
        System.out.println(sc1);
        System.out.println(sc2);
        System.out.println(sc1.getInner());
        System.out.println(sc2.getInner());
        // 输出sc1与sc2的内容
        System.out.println(sc1.getName());
        System.out.println(sc2.getName());
        System.out.println(sc1.getInner().tag);
        System.out.println(sc2.getInner().tag);
        // 是否是浅拷贝
        System.out.println(sc1.getName() == sc2.getName());
        // 修改sc2的内容
        sc2.setName("sc2");
        sc2.getInner().tag = "outer";
        // 输出sc1与sc2的地址
        System.out.println(sc1);
        System.out.println(sc2);
        System.out.println(sc1.getInner());
        System.out.println(sc2.getInner());
        // 输出sc1与sc2的内容
        System.out.println(sc1.getName());
        System.out.println(sc2.getName());
        System.out.println(sc1.getInner().tag);
        System.out.println(sc2.getInner().tag);
    }

编译结果:
在这里插入图片描述
在这里插入图片描述
比较编译结果发现:直接修改name属性,sc1和sc2并不互相影响,但是两者的Inner指向的是同一个,修改sc2对象Inner的tag属性后,sc1也被修改了,说明ShallowClone.Inner用的同一个,想要一刀两断又没有彻底了断,属于藕断丝连的情况,也就是clone不彻底,称之为浅拷贝。

深拷贝

深拷贝顾名思义就是除了是同一类型的外,所有属性都没有联系,新对象是一块完全独立的内存,两者之间没有任何影响。

public class DeepCloneOuter implements Cloneable{
    public DeepCloneInner deepCloneInner;
    @Override
    public Object clone() throws CloneNotSupportedException {
        DeepCloneOuter deepCloneOuter = (DeepCloneOuter) super.clone();
        deepCloneOuter.deepCloneInner = (DeepCloneInner) deepCloneInner.clone();
        return deepCloneOuter;
    }
}
public class DeepCloneInner implements Cloneable, Serializable {
    private String name;

    public String getName() {
        return name;
    }

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

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

测试代码:

    /**
     * 深拷贝
     */
    static void deepClone() {
        System.out.println("*****************************");
        DeepCloneOuter deepCloneOuter = new DeepCloneOuter();
        deepCloneOuter.deepCloneInner = new DeepCloneInner();
        deepCloneOuter.deepCloneInner.setName("source");

        try {
            DeepCloneOuter deepCloneOuter2 = (DeepCloneOuter) deepCloneOuter.clone();
            System.out.println(deepCloneOuter);
            System.out.println(deepCloneOuter2);
            System.out.println(deepCloneOuter.deepCloneInner.getName());
            System.out.println(deepCloneOuter2.deepCloneInner.getName());
            System.out.println(deepCloneOuter.deepCloneInner.getName() == deepCloneOuter2.deepCloneInner.getName());
            System.out.println(deepCloneOuter.deepCloneInner == deepCloneOuter2.deepCloneInner);
            // 修改内容
            deepCloneOuter2.deepCloneInner.setName("clone");
            System.out.println(deepCloneOuter);
            System.out.println(deepCloneOuter2);
            System.out.println(deepCloneOuter.deepCloneInner.getName());
            System.out.println(deepCloneOuter2.deepCloneInner.getName());
            System.out.println(deepCloneOuter.deepCloneInner.getName() == deepCloneOuter2.deepCloneInner.getName());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }

编译结果:

clone.DeepCloneOuter@68de145
clone.DeepCloneOuter@27fa135a
source
source
true
false
clone.DeepCloneOuter@68de145
clone.DeepCloneOuter@27fa135a
source
clone
false
false

在这里插入图片描述
编译结果说明深拷贝拷中的对象除了长得像,初此之外没有任何联系。当拷贝对象比较复杂的时候,该拷贝方法需要单独拷贝很多东西,所以不常用,利用序列化拷贝对象较为常用。

序列化深拷贝

序列化深拷贝是通过序列化对象,之后反序列化,利用字节流写入被拷贝对象并读取后得到新对象,相当于用户参照被拷贝对象手动开辟了一块新内存供新对象使用。
需要拷贝的对象只要实现Serializable即可。

public class SerializationClone implements Serializable {
    private String name;
    private Inner inner;
    public Inner getInner() {
        return inner;
    }
    public void setInner(Inner inner) {
        this.inner = inner;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public static class Inner implements Serializable{
        private String tag;

        public String getTag() {
            return tag;
        }
        public void setTag(String tag) {
            this.tag = tag;
        }
    }
}```

拷贝工具:

```java
public class CloneUtil {

    public static <T extends Serializable> T clone(T objection){
        T cloneObjection = null;
        try {
            // 创建写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            // 写入被拷贝对象
            obs.writeObject(objection);
            // 关闭字节流输入流
            obs.close();

            // 创建字节输入流,分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            // 返回生成的新对象
            cloneObjection = (T) ois.readObject();
            // 关闭对象输入流
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 返回新对象
        return cloneObjection;
    }
}

测试代码:

    /**
     * 序列花拷贝
     */
    static void serializationClone(){
        SerializationClone serializationClone = new SerializationClone();
        serializationClone.setName("serializationClone");
        SerializationClone.Inner inner = new SerializationClone.Inner();
        inner.setTag("serializationCloneTag");
        serializationClone.setInner(inner);
        // 输出原数据
        System.out.println("**************原数据**********");
        System.out.println(serializationClone);
        System.out.println(serializationClone.getInner());
        System.out.println(serializationClone.getName());
        System.out.println(serializationClone.getInner().getTag());

        // 拷贝后
        SerializationClone serializationClone1 = CloneUtil.clone(serializationClone);
        System.out.println("**************拷贝后**********");
        System.out.println(serializationClone1);
        System.out.println(serializationClone1.getInner());
        System.out.println(serializationClone1.getName());
        System.out.println(serializationClone1.getInner().getTag());

        // 修改
        serializationClone1.setName("serializationClone1");
        serializationClone1.getInner().setTag("serializationClone1Tag1");
        // 原数据
        System.out.println("**************修改后原数据**********");
        System.out.println(serializationClone);
        System.out.println(serializationClone.getInner());
        System.out.println(serializationClone.getName());
        System.out.println(serializationClone.getInner().getTag());
        // 拷贝后修改的数据
        System.out.println("**************修改后拷贝数据**********");
        System.out.println(serializationClone1);
        System.out.println(serializationClone1.getInner());
        System.out.println(serializationClone1.getName());
        System.out.println(serializationClone1.getInner().getTag());
    }

编译结果:

**************原数据**********
clone.SerializationClone@26a1ab54
clone.SerializationClone$Inner@3d646c37
serializationClone
serializationCloneTag
**************拷贝后**********
clone.SerializationClone@156643d4
clone.SerializationClone$Inner@123a439b
serializationClone
serializationCloneTag
**************修改后原数据**********
clone.SerializationClone@26a1ab54
clone.SerializationClone$Inner@3d646c37
serializationClone
serializationCloneTag
**************修改后拷贝数据**********
clone.SerializationClone@156643d4
clone.SerializationClone$Inner@123a439b
serializationClone1
serializationClone1Tag1

比较拷贝前后的对象发现两者地址都不一样,包括内部对象的地址,修改任何属性也不会相互影响。

深拷贝建议使用序列化深拷贝
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:16:56  更:2022-02-26 11:19:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:06:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码