Code Ease Code Ease
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档

神秘的鱼仔

你会累是因为你在走上坡路
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档
服务器
  • Java核心基础

    • 基础篇

      • Java常用关键字
      • 写了那么久的String字符串,你可能根本不懂它!
      • 详解Java中的BIO、NIO、AIO
      • 一篇文章带你了解cloneable接口、浅拷贝、深拷贝
        • (一)cloneable接口有什么用
        • (二)什么是深拷贝和浅拷贝
        • (三)浅拷贝代码模拟
        • (四)深拷贝代码模拟
      • 谈一谈session和cookie的区别,以及session的实现机制
      • 什么是接口的幂等性,如何实现接口幂等性?一文搞定
      • Java16都快上线了,你该不会连Java8的特性都不会用吧?
      • Java中的拦截器和过滤器有什么区别
      • 你能保证你的代码没有异常吗?
      • 时间类有多复杂,JDK竟设计了三版
      • JavaIO的整体结构以及File类的使用
      • javaIO之字节流与字符流
    • 集合类

    • JVM虚拟机

    • Java并发

  • 框架的艺术

  • 分布式与微服务

  • 开发经验大全

  • 版本新特性

  • Java
  • Java核心基础
  • 基础篇
CodeEase
2023-09-13
目录

一篇文章带你了解cloneable接口、浅拷贝、深拷贝

作者:鱼仔
博客首页: codeease.top (opens new window)
公众号:Java鱼仔

# (一)cloneable接口有什么用

我们都知道想要实现拷贝需要实现Cloneable接口并在类中实现clone()方法,不过比较神奇的是,clone()方法并不是Cloneable接口中的方法。

Cloneable接口是一个空接口,里面没有任何内容

1.png

但是如果没有实现Cloneable接口,就会导致clone()方法报CloneNotSupportException错误,所以你可以把Cloneable接口看成实现clone()方法必须要的一个因素。

# (二)什么是深拷贝和浅拷贝

开发过程中,有时会遇到把现有的一个对象的所有成员属性拷贝给另一个对象的需求。这个时候就会用到拷贝这个概念。我们把原对象定义成A,拷贝后的对象定义成B,如果只是单纯使用clone方法进行拷贝,你会发现:

1、对于八个基本类型,会拷贝其值,并且B的改变不会影响A。

2、如果是一个对象,拷贝的是地址引用,也就是说此时新拷贝出的对象与原有对象共享该实例变量,不受访问权限的限制。B对该值的改变会影响A。

3、对于String字符串,这个比较特殊,虽然拷贝的也是引用,但是在修改的时候,它会从字符串池中重新生成新的字符串,原有的字符串对象保持不变。

这种只单纯拷贝引用地址的动作就是浅拷贝。

相反,如果拷贝一个对象时不是简单的将地址引用拷贝出来,而是新建了一个对象,这种方式就是深拷贝。

# (三)浅拷贝代码模拟

通过代码模拟浅拷贝的过程:

首先,新建两个实体类,学生和老师:

public class Teacher {
    private int id;
    private String name;
    //省略构造方法、get、set、toString方法
}
1
2
3
4
5

接下来是学生,学生的实体类需要实现clone

public class Student implements Cloneable {
    private int id;
    private String name;
    private Teacher teacher;
    //省略构造方法、get、set、toString方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
1
2
3
4
5
6
7
8
9
10

接下来就来看一下浅拷贝的效果

public static void main(String[] args) throws CloneNotSupportedException {
    //新建一个student1
    Student student1=new Student(1,"javayz",new Teacher(1,"teacher1"));
    //student2从student1中克隆过去
    Student student2= (Student) student1.clone();
    //修改基本类型 int
    student2.setId(2);
    //修改String
    student2.setName("javayz2");
    //修改对象类型teacher
    Teacher teacher = student2.getTeacher();
    teacher.setName("teacher2");
    System.out.println(student1);
    System.out.println(student2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

2.png

通过结果就可以发现,修改被克隆对象的基本类型和String类型不会对原来数据造成影响,但是由于用的是同一个引用地址,修改对象时两边都会被修改。

# (四)深拷贝代码模拟

深拷贝的其中一个方法是把被拷贝对象中的所有引用类型也都实现深拷贝,最后逐层拷贝实现引用地址是新的而不是用的同一个。

修改上面的teacher对象代码,实现clone方法

public class Teacher implements Cloneable{
    private int id;
    private String name;
    //省略构造方法、get、set、toString方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
1
2
3
4
5
6
7
8
9

修改student类的clone方法

@Override
protected Object clone() throws CloneNotSupportedException {
    Student student = (Student) super.clone();
    student.teacher= (Teacher) teacher.clone();
    return student;
}
1
2
3
4
5
6

然后执行同样的测试代码后就会发现两个对象已经互相不影响了。

第二个方法是利用serializable实现深拷贝,这种方式的原理在于通过IO流的方式先将序列化后的对象写进IO流中,再取出来实现深拷贝。这种方式下所有涉及到的类都必须实现Serializable接口

新建一个DeepStudent类

public class DeepStudent implements Serializable {
    private static final long serialVersionUID=1L;
    private int id;
    private String name;
    private Teacher teacher;
    //省略构造方法、get、set、toString方法
    public Object deepCopy(){
        try {
            //将对象写到IO流中
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);
            //再从IO流中获取到对象
            ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bis);
            return ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

编写一个测试方法:

    public static void main(String[] args) throws CloneNotSupportedException {
       //新建一个student1
        DeepStudent student1=new DeepStudent(1,"javayz",new Teacher(1,"teacher1"));
        //student2从student1中克隆过去
        DeepStudent student2= (DeepStudent) student1.deepCopy();
        //修改基本类型 int
        student2.setId(2);
        //修改String
        student2.setName("javayz2");
        //修改对象类型teacher
        Teacher teacher = student2.getTeacher();
        teacher.setName("teacher2");
        System.out.println(student1);
        System.out.println(student2);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上次更新: 2025/04/29, 17:22:06
详解Java中的BIO、NIO、AIO
谈一谈session和cookie的区别,以及session的实现机制

← 详解Java中的BIO、NIO、AIO 谈一谈session和cookie的区别,以及session的实现机制→

最近更新
01
AI大模型部署指南
02-18
02
半个月了,DeepSeek为什么还是服务不可用
02-13
03
Python3.9及3.10安装文档
01-23
更多文章>
Theme by Vdoing | Copyright © 2023-2025 备案图标 浙公网安备33021202002405 | 浙ICP备2023040452号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式