Java中的深克隆和浅克隆——Cloneable接口

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介:

一、没有使用克隆带来的问题

public class CloneTest

{

static Student s = new Student("aaa", 20);


// 直接赋值带来的问题

public static void noclone()

{

// 传的是引用的副本,改变了noCloneStudent也改变了s

Student noCloneStudent = new Student();

noCloneStudent = s;

noCloneStudent.setName("bbb");

noCloneStudent.setAge(21);

System.out.println(s);

System.out.print(noCloneStudent);

}

}

Student [name=bbb, age=21]

Student [name=bbb, age=21]

本意是设置noCloneStudent中的值,但把原来的对象s中的值也改变了。


二、浅克隆与深克隆

①浅克隆——若要克隆Student对象,只克隆他自身以及他包含的所有对象的引用地址。

②深克隆——克隆除自身以外所有的对象,包括自身所包含的所有对象实例。由具体的需求决定深克隆的层次(N层克隆)。


三、浅克隆

public class Student implements Cloneable

{

private String name;

private int age;


public Student()

{

super();

}


public Student(String name, int age)

{

super();

this.name = name;

this.age = age;

}



// 浅克隆

protected Object clone() throws CloneNotSupportedException

{

return super.clone();

}


public String toString()

{

return "Student [name=" + name + ", age=" + age + "]";

}

......

}


public class CloneTest

{

static Student s = new Student("aaa", 20);

// 浅克隆

public static void test0() throws Exception

{

Student ss = (Student) s.clone();

ss.setName("bbb");

ss.setAge(21);

System.out.println(s);

System.out.print(ss);

}

}

Student [name=aaa, age=20]

Student [name=bbb, age=21]

克隆的效果就达到了。


四、深克隆

public class TClass implements Cloneable

{

private Student stu;

private String name;

private List<String> courses = new ArrayList<String>();


public TClass()

{

super();

}


public TClass(Student stu, String name, List<String> courses)

{

super();

this.stu = stu;

this.name = name;

this.courses = courses;

}


// 深克隆

public Object clone() throws CloneNotSupportedException

{

TClass tea = (TClass) super.clone();

tea.stu = (Student) tea.stu.clone();

return tea;

}


public String toString()

{

return "TClass [stu=" + stu + ", name=" + name + ", courses=" + courses + "]";

}

......

}

// 深克隆

public static void test1() throws Exception

{

TClass t = new TClass();

t.setStu(s);

t.setName("张老师");

t.getCourses().add("JAVA");

t.getCourses().add("C#");

System.out.println(t);

TClass tt = (TClass) t.clone();

Student sss = (Student) s.clone();

sss.setName("bbb");

sss.setAge(21);

tt.setStu(sss);

tt.setName("李老师");

tt.getCourses().add("oracle");

tt.getCourses().add("mysql");

System.out.println(t);

System.out.println(tt);

}

TClass [stu=Student [name=aaa, age=20], name=张老师, courses=[JAVA, C#]]

TClass [stu=Student [name=aaa, age=20], name=张老师, courses=[JAVA, C#, oracle, mysql]]

TClass [stu=Student [name=bbb, age=21], name=李老师, courses=[JAVA, C#, oracle, mysql]]

动态数组还是保留了原来的引用,所以其数组没有达到拷贝的效果。


改造一下,让其中的数组有新的引用

public class TClass implements Cloneable

{

private Student stu;

private String name;

private List<String> courses = new ArrayList<String>();


public TClass()

{

super();

}


public TClass(Student stu, String name, List<String> courses)

{

super();

this.stu = stu;

this.name = name;

this.courses = courses;

}


// 深克隆(更深一层)

public Object clone() throws CloneNotSupportedException

{

TClass tea = (TClass) super.clone();

tea.stu = (Student) tea.stu.clone();

tea.courses = new ArrayList<String>(); // 指向不同引用

return tea;

}


public String toString()

{

return "TClass [stu=" + stu + ", name=" + name + ", courses=" + courses + "]";

}

......

}

TClass[stu=Student [name=aaa, age=20], name=张老师, courses=[Java, C#]]

TClass[stu=Student [name=bbb, age=21], name=李老师, courses=[oracle, mysql]]


再改造一下,希望动态数组既有了新的引用,又保留了原值

public class TClass3 implements Cloneable

{

private Student stu;

private String name;

private List<String> courses = new ArrayList<String>();


public TClass()

{

super();

}


public TClass(Student stu, String name, List<String> courses)

{

super();

this.stu = stu;

this.name = name;

this.courses = courses;

}


// 深克隆(更深一层)

public Object clone() throws CloneNotSupportedException

{

TClass tea = (TClass) super.clone();

tea.stu = (Student) tea.stu.clone();

// 指向不同引用

tea.courses = new ArrayList<String>();

// 又想保留原有的值

for (int i = 0; i < courses.size(); i++)

{

tea.courses.add(courses.get(i));

}

return tea;

}


public String toString()

{

return "TClass [stu=" + stu + ", name=" + name + ", courses=" + courses + "]";

}

......

}

Teacher [stu=Student [name=aaa, age=20], name=张老师, courses=[Java, C#]]

Teacher [stu=Student [name=bbb, age=21], name=李老师, courses=[Java, C#, oracle, spring]]

本文转自IT徐胖子的专栏博客51CTO博客,原文链接http://blog.51cto.com/woshixy/1409503如需转载请自行联系原作者

woshixuye111
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
6月前
|
消息中间件 缓存 前端开发
从资损百万到零事故:Java 接口幂等设计的艺术与实践
在分布式系统中,重复请求常引发严重资损,如支付双扣、库存超卖等问题,其根源在于接口缺乏幂等性设计。本文通过真实案例揭示幂等性的重要性,并详解8种主流解决方案,涵盖唯一请求ID、乐观锁、悲观锁、状态机等,帮助开发者构建稳定系统,保障业务一致性。无论你是架构师还是开发工程师,都能从中获得实战指导,有效规避重复调用带来的风险。
565 2
|
6月前
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
5月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
194 11
|
4月前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
262 0
|
6月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
447 23
|
6月前
|
安全 Java 开发者
Java集合框架:详解Deque接口的栈操作方法全集
理解和掌握这些方法对于实现像浏览器后退功能这样的栈操作来说至关重要,它们能够帮助开发者编写既高效又稳定的应用程序。此外,在多线程环境中想保证线程安全,可以考虑使用ConcurrentLinkedDeque,它是Deque的线程安全版本,尽管它并未直接实现栈操作的方法,但是Deque的接口方法可以相对应地使用。
365 12
|
6月前
|
存储 安全 Java
Java集合框架(一):List接口及其实现类剖析
本文深入解析Java中List集合的实现原理,涵盖ArrayList的动态数组机制、LinkedList的链表结构、Vector与Stack的线程安全性及其不推荐使用的原因,对比了不同实现的性能与适用场景,帮助开发者根据实际需求选择合适的List实现。
|
6月前
|
Java API 网络架构
java调用api接口自动判断节假日信息
java调用api接口自动判断节假日信息
2288 0
|
7月前
|
存储 安全 Java
深入理解Java序列化接口及其实现机制
记住,序列化不仅仅是把对象状态保存下来那么简单,它涉及到类的版本控制、安全性和性能等多个重要方面。正确理解和实现Java序列化机制对于构建高效、安全和可维护的Java应用至关重要。
250 0