什么是Java浅拷贝和深拷贝

发布时间:2021-10-29 15:23:59 作者:iii
来源:亿速云 阅读:108

这篇文章主要讲解了“什么是Java浅拷贝和深拷贝”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是Java浅拷贝和深拷贝”吧!

问题

“如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢?首先, 你必须新建一个属于相同类的对象。然后,  你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。

for (int i = 0; i < 10; i++) {   Sheep sheep = new Sheep("肖恩"+i+"号",2+i,"白色");   System.out.println(sheep.toString()); }

这种方式是比较容易想到的,但是有几个不足

“万物兼对象的 Java 中的所有类的根类 Object,提供了一个 clone() 方法,该方法可以将一个 Java 对象复制一份,但是需要实现  clone() 的类必须要实现一个接口 Cloneable,该接口表示该类能够复制且具有复制的能力。这就引出了原型模式。

基本介绍

  1. 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

  2. 原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类

  3. 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即  对象**.clone**()

类图

什么是Java浅拷贝和深拷贝

Java 中 Prototype 类需要具备以下两个条件

  1. 实现 Cloneable 接口。在 Java 语言有一个 Cloneable  接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法。在 Java  虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常

  2. 重写 Object 类中的 clone 方法。Java 中,所有类的父类都是 Object 类,Object 类中有一个 clone  方法,作用是返回对象的一个拷贝

实例

“我们用王二小放羊的例子写这个实例

1、原型类(实现 Clonable)

@Setter @Getter @NoArgsConstructor @AllArgsConstructor class Sheep implements Cloneable {     private String name;     private Integer age;     private String color;      @Override     protected Sheep clone() {         Sheep sheep = null;         try {             sheep = (Sheep) super.clone();         } catch (Exception e) {             System.out.println(e.getMessage());         }         return sheep;     } }

2、具体原型

按业务的不同实现不同的原型对象,假设现在主角是王二小,羊群里有山羊、绵羊一大群

public class Goat extends Sheep{     public void graze() {         System.out.println("山羊去吃草");     } }
public class Lamb extends Sheep{     public void graze() {         System.out.println("羔羊去吃草");     } }

3、客户端

public class Client {      static List<Sheep> sheepList = new ArrayList<>();     public static void main(String[] args) {         Goat goat = new Goat();         goat.setName("山羊");         goat.setAge(3);         goat.setColor("灰色");         for (int i = 0; i < 5; i++) {             sheepList.add(goat.clone());         }          Lamb lamb = new Lamb();         lamb.setName("羔羊");         lamb.setAge(2);         lamb.setColor("白色");         for (int i = 0; i < 5; i++) {             sheepList.add(lamb.clone());             System.out.println(lamb.hashCode()+","+lamb.clone().hashCode());         }          for (Sheep sheep : sheepList) {             System.out.println(sheep.toString());         } }

原型模式将克隆过程委派给被克隆的实际对象。模式为所有支持克隆的对象声明了一个通用接口,  该接口让你能够克隆对象,同时又无需将代码和对象所属类耦合。通常情况下,这样的接口中仅包含一个 克隆方法。

所有的类对 克隆方法的实现都非常相似。该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。你甚至可以复制私有成员变量,  因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。

支持克隆的对象即为原型。当你的对象有几十个成员变量和几百种类型时, 对其进行克隆甚至可以代替子类的构造。

优势

使用原型模式创建对象比直接 new 一个对象在性能上要好的多,因为 Object 类的 clone  方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。

使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。

因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。

适用场景

《Head First 设计模式》是这么形容原型模式的:当创建给定类的实例的过程很昂贵或很复杂时,就是用原型模式。

如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。

如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。别人创建这些子类的目的可能是为了创建特定类型的对象

原型模式在 Spring 中的应用

我们都知道 Spring bean 默认是单例的,但是有些场景可能需要原型范围,如下

<bean id="sheep" class="priv.starfish.prototype.Sheep" scope="prototype">    <property name="name" value="肖恩"/>    <property name="age" value="2"/>    <property name="color" value="白色"/> </bean>

同样,王二小还是有 10 只羊,感兴趣的也可以看下他们创建的对象是不是同一个

public class Client {     public static void main(String[] args) {         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");         for (int i = 0; i < 10; i++) {             Object bean = context.getBean("sheep");             System.out.println(bean);         }     } }

感兴趣的同学可以深入源码看下具体的实现,在 AbstractBeanFactory 的 doGetBean() 方法中

什么是Java浅拷贝和深拷贝

原型模式的注意事项

浅拷贝和深拷贝

首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。

在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 『 =  』号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。

而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

“所谓的浅拷贝和深拷贝,只是在拷贝对象的时候,对 类的实例对象 这种引用数据类型的不同操作而已

浅拷贝

  1. 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。

  2. 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值

  3. 前面我们克隆羊就是浅拷贝,如果我们在 Sheep 中加一个对象类型的属性,public Sheep child;可以看到 s 和 s1 的 friend  是同一个。

 Sheep s = new Sheep();   s.setName("sss");      s.friend = new Sheep();   s.friend.setName("喜洋洋");      Sheep s1 = s.clone();   System.out.println(s == s1);   System.out.println(s.hashCode()+"---"+s.clone().hashCode());      System.out.println(s.friend == s1.friend);   System.out.println(s.friend.hashCode() + "---" +s1.friend.hashCode()); false 621009875---1265094477 true 2125039532---2125039532

深拷贝

现在我们知道 clone() 方法,只能对当前对象进行浅拷贝,引用类型依然是在传递引用。那如何进行一个深拷贝呢?

常见的深拷贝实现方式有两种:

  1. 重写 clone 方法来实现深拷贝

  2. 通过对象序列化实现深拷贝

浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用  clone() 方法就是一次浅拷贝的操作。

感谢各位的阅读,以上就是“什么是Java浅拷贝和深拷贝”的内容了,经过本文的学习后,相信大家对什么是Java浅拷贝和深拷贝这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

推荐阅读:
  1. 深拷贝&浅拷贝
  2. javascript深拷贝和浅拷贝详解

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:如何理解Java语言

下一篇:Mysql数据分组排名实现的示例分析

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》