Java浅拷贝和深拷贝的方式

article/2025/10/5 14:15:11

文章目录

    • 1. 前言
    • 2. 概念介绍
      • 2.1 拷贝 / 克隆的概念
      • 2.2 为什么需要拷贝方法?
      • 2.3 什么是浅拷贝?浅拷贝和深拷贝的区别是什么?
    • 3. 深拷贝的实现方式
      • 3.1 手动深拷贝
      • 3.2 序列化方式
        • 3.2.1 自定义序列化工具函数
        • 3.2.2 commons-lang3 的序列化工具类
        • 3.2.3 JSON 序列化
    • 4. 总结
    • 5. 课后题
      • 5.1 亲手尝试
        • 5.1.1 实现浅拷贝
        • 5.1.2 实现深拷贝
    • 6. 个人感悟/总结
      • 6.1.1 原文摘录
      • 6.1.2 自我感悟

没有智慧的头脑,就像没有蜡烛的灯笼。 ——托尔斯泰

1. 前言

《手册》第 10 页有关于 Objectclone 问题的描述 1:

【推荐】慎用 Object 的 clone 方法来拷贝对象。
说明:对象 clone 方法默认是浅拷贝,若想实现深拷贝需覆写 clone 方法实现域对象的深度遍历式拷贝。

那么我们要思考几个问题:

  1. 什么是浅拷贝?
  2. 浅拷贝和深拷贝的区别是什么?
  3. 拷贝的目的是什么?
  4. 拷贝的使用场景是什么?
  5. 如何实现深拷贝?

网上也有很多介绍浅拷贝和深拷贝的文章,但文章质量参差不齐,有些文章读完仍然对概念得理解非常含糊。读完这些文章对拷贝的使用场景,对深拷贝的实现方式等都无法有全面和深刻的理解。

为此本节将带着大家系统地研究这上述问题,以便大家未来遇到类似问题时可以举一反三,灵活迁移。

2. 概念介绍

2.1 拷贝 / 克隆的概念

我们先研究第 1 个问题:什么是拷贝?

维基百科对 “克隆” 的描述如下 2:

克隆 (英语: Clone) 在广义上是指利用生物技术由无性生殖产生与原原个体有完全相同基因组之后代的过程。

在园艺学上,克隆指通过营养繁殖产生的单一植株的后代,很多植物都是通过克隆这样的无性繁殖方式从单一植株获得大量的子代个体。

在生物学上,是指选择性地复制出一段 DNA 序列(分子克隆)、细胞(细胞克隆)或个体(个体克隆)。

克隆一个生物体意味着创造一个与原先的生物体具有完全一样的遗传信息的新生物体。

计算机中的拷贝或克隆和上述概念很类似,可以类比理解。

对象的拷贝,就是根据原来的对象 “复制” 一份属性、状态一致的新的对象。

2.2 为什么需要拷贝方法?

我们思考第 2 个问题:为什么需要拷贝呢?

我们来看下面的订单类和商品类。

订单类( Order ):

@Data
public class Order {private Long id;private String orderNo;private List<Item> itemList;
}

商品类( Item ):

@Data
public class Item {private Long id;private Long itemId;private String name;private String desc;// 省略其他
}

如果我们查询得到 1 个订单对象,该对象包括 6 个商品对象。

如果我们还需要构造多个新的订单对象,属性和上述订单对象非常相似,只是订单号不同或者商品略有区别。

这时如果有一个 “复制” 方法,可以将订单复制一个副本,而且修改副本中的订单号和商品列表 ( itemList ) 不影响原始对象,是不是很方便?

另外一个非常典型的场景是在多线程中。如果只用一个主线程,在主线程中修改订单号分别调用 doSomeThing 函数,想分别打印 first 和 second 两个订单编号字符串。

@Slf4j
public class CloneDemo { public static void main(String[] args) {Order order = OrderMocker.mock();order.setOrderNo("first");doSomeThing(order);order.setOrderNo("second");doSomeThing(order);}private static void doSomeThing(Order order) {try {TimeUnit.SECONDS.sleep(1L);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(order.getOrderNo());}
}

运行程序后输出的结果的确是: firstsecond

但在多线程环境中,如果我们不通过克隆构造新的对象,线程池中两个线程会公用同一个对象,后面对订单号的修改将影响到其它线程。

@Slf4j
public class CloneDemo {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);Order order = OrderMocker.mock();order.setOrderNo("first");executorService.execute(() -> doSomeThing(order));order.setOrderNo("second");executorService.execute(() -> doSomeThing(order));}private static void doSomeThing(Order order) {try {TimeUnit.SECONDS.sleep(1L);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(order.getOrderNo());}
}

输出的结果是: secondsecond

因此如果能够克隆一个新的对象,并且对新对象的修改不影响原始对象,就能实现我们期待的效果。

2.3 什么是浅拷贝?浅拷贝和深拷贝的区别是什么?

通过前言部分的介绍,我们知道 Objectclone 函数默认是浅拷贝。

按照惯例我们进入源码,看看是否能够得到我们想要的答案:

/*** Creates and returns a copy of this object.  The precise meaning* of "copy" may depend on the class of the object. The general* intent is that, for any object {@code x}, the expression:* <blockquote>* <pre>* x.clone() != x</pre></blockquote>* will be true, and that the expression:* <blockquote>* <pre>* x.clone().getClass() == x.getClass()</pre></blockquote>* will be {@code true}, but these are not absolute requirements.* While it is typically the case that:* <blockquote>* <pre>* x.clone().equals(x)</pre></blockquote>* will be {@code true}, this is not an absolute requirement.* <p>* By convention, the returned object should be obtained by calling* {@code super.clone}.  If a class and all of its superclasses (except* {@code Object}) obey this convention, it will be the case that* {@code x.clone().getClass() == x.getClass()}.* <p>* By convention, the object returned by this method should be independent* of this object (which is being cloned).  To achieve this independence,* it may be necessary to modify one or more fields of the object returned* by {@code super.clone} before returning it.  Typically, this means* copying any mutable objects that comprise the internal "deep structure"* of the object being cloned and replacing the references to these* objects with references to the copies.  If a class contains only* primitive fields or references to immutable objects, then it is usually* the case that no fields in the object returned by {@code super.clone}* need to be modified.* <p>* The method {@code clone} for class {@code Object} performs a* specific cloning operation. First, if the class of this object does* not implement the interface {@code Cloneable}, then a* {@code CloneNotSupportedException} is thrown. Note that all arrays* are considered to implement the interface {@code Cloneable} and that* the return type of the {@code clone} method of an array type {@code T[]}* is {@code T[]} where T is any reference or primitive type.* Otherwise, this method creates a new instance of the class of this* object and initializes all its fields with exactly the contents of* the corresponding fields of this object, as if by assignment; the* contents of the fields are not themselves cloned. Thus, this method* performs a "shallow copy" of this object, not a "deep copy" operation.* <p>* The class {@code Object} does not itself implement the interface* {@code Cloneable}, so calling the {@code clone} method on an object* whose class is {@code Object} will result in throwing an* exception at run time.** @return     a clone of this instance.* @throws  CloneNotSupportedException  if the object's class does not*               support the {@code Cloneable} interface. Subclasses*               that override the {@code clone} method can also*               throw this exception to indicate that an instance cannot*               be cloned.* @see java.lang.Cloneable*/
protected native Object clone() throws CloneNotSupportedException;

该函数给出了非常详尽的介绍。下面给出一些要点的翻译:

该方法是创建对象的副本。这就意味着 “副本” 依赖于该对象的类型。

对于任何对象而言,一般来说下面的表达式成立:

x.clone() != x 的结果为 true

x.clone().getClass() == x.getClass() 的结果为 true

但是这些也不是强制的要求。

x.clone().equals(x) 的结果也是 true。这也不是强制要求。

按照惯例,返回对象应该通过调用 super.clone 函数来构造。如果一个类和它的所有父类(除了 Object )都遵循这个约定,那么 x.clone().getClass() == x.getClass() 将成立。

按照惯例,返回的对象应该和原始对象是独立的。

为了实现这种独立性,后续应该在调用 super.clone 得到拷贝对象并返回之前,应该对内部深层次的可变对象创建副本并指向克隆对象的对应属性的引用。

如果一个类只包含基本类型的属性或者指向不可变对象的引用,这种情况下,super.clone 返回的对象不需要被修改。

如果调用 clone 函数的类没有实现 Cloneable 接口将会抛出 CloneNotSupportedException

注意所有的数组对象都默认实现了 Cloneable 接口。

该函数会创建该类的新实例,并初始化所有属性对象。属性对象本身并不会自动调用 clone

因此此方法实现的是浅拷贝而不是深拷贝。

因此我们可以了解到,浅拷贝将返回该类的新的实例,该实例的引用类型对象共享。
深拷贝也会返回该类的新的实例,但是该实例的引用类型属性也是拷贝的新对象。

如果用一句话来描述,浅拷贝和深拷贝的主要区别在于对于引用类型是否共享。

为了更好地理解浅拷贝,我们给出一个示例:

@Data
public class Order implements Cloneable {private Long id;private String orderNo;private List<Item> itemList;@Overridepublic Order clone() {try {return (Order)super.clone();} catch (CloneNotSupportedException ignore) {// 不会调到这里}return null;}
}

通过 Object 类的 clone 函数的注释我们了解到:如果调用 clone 函数的类没有实现 Cloneable 接口将会抛出 CloneNotSupportedException

因此要实现 Cloneable 接口。

重写 clone 函数是为了供外部使用,因此定义为 public

返回值类型定义为客户端直接需要的对象类型(本类)。

这体现了《Effective Java》的 Item 11 中所提到的 3:

Never make the client do anything the library can do for the client.

不要让客户端去做任何类库可以替它完成的事。

我们为上述浅拷贝编写测试代码:

public class OrderMocker {public static Order mock() {Order order = new Order();order.setId(1L);order.setOrderNo("abcdefg");List<Item> items = new ArrayList<>();Item item = new Item();item.setId(0L);item.setItemId(0L);item.setName("《阿里巴巴Java开发手册》详解慕课专栏");item.setDesc("精品推荐");items.add(item);order.setItemList(items);return order;}
}
 @Testpublic void shallowClone() {Order order = OrderMocker.mock();Order cloneOrder = order.clone();assertFalse(order == cloneOrder);assertTrue(order.getItemList() == cloneOrder.getItemList());}

该单元测试可以通过,从而证实了 clone 函数的注释,证实了浅拷贝的表现。

即浅拷贝后,原对象的订单列表和克隆对象的订单列表地址相同。

因此如果使用浅拷贝,修改拷贝订单的商品列表,那么原始订单对象的商品列表也会受到影响。

为了更形象地理解浅拷贝和深拷贝的概念,我们以文件夹进行类比:

浅拷贝:同一个文件夹的两个快捷方式,虽然是两个不同的快捷方式,但是指向的文件夹是同一个,不管是通过哪个快捷方式进入,对该文件夹下的文件修改,相互影响。

深拷贝:我们复制某个文件夹(含里面的内容)在另外一个目录进行粘贴,就可得到具有相同内容的新目录,对新文件夹修改不影响原始文件夹。

3. 深拷贝的实现方式

虽然浅拷贝能够实现拷贝的功能,但是浅拷贝的引用类型成员变量是共享的,修改极可能导致相互影响。

业务开发中使用深拷贝更多一些,那么实现深拷贝有哪些方式呢?

3.1 手动深拷贝

@Data
public class Order implements Cloneable {private Long id;private String orderNo;private List<Item> itemList;@Overridepublic Order clone() {try {Order order = (Order) super.clone();if (id != null) {order.id = new Long(id);}if (orderNo != null) {order.orderNo = new String(orderNo);}if (itemList != null) {List<Item> items = new ArrayList<>();for (Item each : itemList) {Item item = new Item();Long id = each.getId();if(id != null){item.setId(new Long(id));}Long itemId = each.getItemId();if(itemId != null){item.setItemId(new Long(itemId));}String name = each.getName();if(name != null){item.setName(new String(name));}String desc = each.getDesc();if(desc != null){item.setDesc(new String(desc));}items.add(item);}order.setItemList(items);}return order;} catch (CloneNotSupportedException ignore) {}return null;}
}

深拷贝也调用 super.clone 是为了支撑 x.clone().getClass() == x.getClass()

写好代码后,通过调用 Order 类的 clone 函数即可实现深拷贝。

由于克隆的对象和内部的引用类型的属性全部都是依据原始对象新建的对象,因此如果修改拷贝对象的商品列表,原始订单对象的商品列表并不会受到影响。

通过下面的单元测试来验证:

@Test
public void deepClone() {Order order = OrderMocker.mock();Order cloneOrder = (Order) order.clone();assertFalse(order == cloneOrder);assertFalse(order.getItemList() == cloneOrder.getItemList());
}

该单测可顺利通过。

3.2 序列化方式

前面章节我们讲到了序列化和反序列化的知识,讲到了序列化的主要使用场景包括深拷贝。

序列化通过将原始对象转化为字节流,再从字节流重建新的 Java 对象,因此原始对象和反序列化后的对象修改互不影响。

因此可以使用之前讲到的序列化和反序列化方式来实现深拷贝。

3.2.1 自定义序列化工具函数

如果我们不想为了深拷贝这一项功能就依赖新的 jar 包,可以在自己项目中借助对象输入和输出流编写拷贝工具函数。

示例代码如下:

  	  /*** JDK序列化方式深拷贝*/public static <T> T deepClone(T origin) throws IOException, ClassNotFoundException {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);)        {objectOutputStream.writeObject(origin);objectOutputStream.flush();}byte[] bytes = outputStream.toByteArray();try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);) {return JdkSerialUtil.readObject(inputStream);}}

我们可通过调试查看克隆对象和原始对象。

下图中我们可以清晰地看到,通过此方法克隆得到的新的对象是一个全新的对象。

需要注意的是:正如前面章节所讲,Java 序列化需要实现 Serializable 接口,而且效率不是特别高。

3.2.2 commons-lang3 的序列化工具类

我们可以利用项目中引用的常见工具包的工具类实现深拷贝,避免重复造轮子。

可以使用 commons-lang3 (3.7 版本)的序列化工具类: org.apache.commons.lang3.SerializationUtils#clone

@Test
public void serialUtil() {Order order = OrderMocker.mock();// 使用方式Order cloneOrder = SerializationUtils.clone(order);assertFalse(order == cloneOrder);assertFalse(order.getItemList() == cloneOrder.getItemList());
}

前面反复提到过,我们学习知识不仅要知其然,而且要知其所以然。

那么它是如何实现深拷贝的呢?

按照惯例我们打开源码:

/*** <p>Deep clone an {@code Object} using serialization.</p>** <p>This is many times slower than writing clone methods by hand* on all objects in your object graph. However, for complex object* graphs, or for those that don't support deep cloning this can* be a simple alternative implementation. Of course all the objects* must be {@code Serializable}.</p>** @param <T> the type of the object involved* @param object  the {@code Serializable} object to clone* @return the cloned object* @throws SerializationException (runtime) if the serialization fails*/
public static <T extends Serializable> T clone(final T object) {if (object == null) {return null;}final byte[] objectData = serialize(object);final ByteArrayInputStream bais = new ByteArrayInputStream(objectData);try (ClassLoaderAwareObjectInputStream in = new ClassLoaderAwareObjectInputStream(bais,object.getClass().getClassLoader())) {/** when we serialize and deserialize an object,* it is reasonable to assume the deserialized object* is of the same type as the original serialized object*/@SuppressWarnings("unchecked") // see abovefinal T readObject = (T) in.readObject();return readObject;} catch (final ClassNotFoundException ex) {throw new SerializationException("ClassNotFoundException while reading cloned object data", ex);} catch (final IOException ex) {throw new SerializationException("IOException while reading or closing cloned object data", ex);}
}

通过其返回值的泛型描述 `` 可以断定参数对象需要实现序列化接口。

该函数注释也给出了性能说明,该深拷贝方法性能不如直接手动写 clone 方法效率高。

大家可以进到该方法的子函数中查看更多细节。

通过源码的分析我们发现,该克隆函数本质上也是通过 Java 序列化和反序列化方式实现。

3.2.3 JSON 序列化

我们还可以通过 JSON 序列化方式实现深拷贝。

下面我们利用 Google 的 Gson 库(2.8.5 版本),实现基于 JSON 的深拷贝:

首先我们将深拷贝方法封装到拷贝工具类中:

/*** Gson方式实现深拷贝*/
public static <T> T deepCloneByGson(T origin, Class<T> clazz) {Gson gson = new Gson();return gson.fromJson(gson.toJson(origin), clazz);
}

使用时直接调用封装的工具方法即可:

 @Testpublic void withGson() {Order order = OrderMocker.mock();// gson序列化方式Order cloneOrder = CloneUtil.deepCloneByGson(order, Order.class);assertFalse(order == cloneOrder);assertFalse(order.getItemList() == cloneOrder.getItemList());}

使用 JSON 序列化方式实现深拷贝的好处是,性能比 Java 序列化方式更好,更重要的是不要求序列化对象以及成员属性(嵌套)都要实现序列化接口。

我们也可以使用前面讲到的 Hessian 和 Kryo 序列化来实现,请大家自行封装。

上面通过 Gson 实现的深拷贝工具方法封装,再次体现了 “不要让客户端去做任何类库可以替它完成的事” 的原则。

这点也和《重构 - 改善既有代码的设计》 第一版 10.13 封装向下转型的重构方案一致。

最后,建议不管采取哪种或者哪几种深拷贝方式,都尽量将其封装到项目的克隆工具类中,方便复用

4. 总结

本节重点讲述了浅拷贝和深拷贝的概念,它们的主要区别,以及浅拷贝和深拷贝的实现方式。

5. 课后题

请自定义一个类,编写代码分别实现浅拷贝和深拷贝。

5.1 亲手尝试

5.1.1 实现浅拷贝

创建商品类Goods

@Data
public class Goods implements Cloneable {private Long id;private String title;private String desc;private List<GoodsSku> goodsSkuList;@Overridepublic Goods clone() {try {return (Goods)super.clone();} catch (CloneNotSupportedException ignore) {// 不会调到这里}return null;}
}

创建商品属性类GoodsSku

@Data
public class GoodsSku {private Long id;private Long GoodsId;private String name;}

之后再创建mock Goods对象的工具类

public class GoodsMocker {public static Goods mock() {Goods goods = new Goods();goods.setId(1L);goods.setTitle("Asus/华硕天选");goods.setDesc("Asus/华硕天选 FA506 锐龙8核 吃鸡学生游戏手提笔记本电脑144Hz");List<GoodsSku> goodsSkuList = new ArrayList<>();GoodsSku goodsSku = new GoodsSku();goodsSku.setId(0L);goodsSku.setGoodsId(1L);goodsSku.setName("512G SDD+16G大内存+AMD R7-4800H");goodsSkuList.add(goodsSku);goods.setGoodsSkuList(goodsSkuList);return goods;}
}

单元测试代码:

public class Test04 {@Testpublic void shallowClone() {Goods goods = GoodsMocker.mock();Goods cloneGoods = goods.clone();Assert.assertFalse(goods == cloneGoods);Assert.assertTrue(goods.getGoodsSkuList() == cloneGoods.getGoodsSkuList());}}

运行单测,结果通过,如图。

5.1.2 实现深拷贝

修改Goods的clone()方法:

@Overridepublic Goods clone() {try {Goods goods = (Goods)super.clone();if (id !=null) {goods.id = new Long(id);}if (title != null) {goods.title = title;}if (desc != null) {goods.desc = desc;}if (goodsSkuList!=null) {List<GoodsSku> goodsSkus = new ArrayList<>();for (GoodsSku each : goodsSkuList) {GoodsSku goodsSku = new GoodsSku();Long id = each.getId();if (id != null) {goodsSku.setId(id);}Long goodsId = each.getGoodsId();if (goodsId != null) {goodsSku.setGoodsId(goodsId);}String name = each.getName();if (name != null) {goodsSku.setName(name);}goodsSkus.add(goodsSku);} // end forgoods.setGoodsSkuList(goodsSkus);}return goods;} catch (CloneNotSupportedException ignore) {// 不会调到这里}return null;}

单元测试代码:

public class Test04 {@Testpublic void shallowClone() {Goods goods = GoodsMocker.mock();Goods cloneGoods = goods.clone();Assert.assertFalse(goods == cloneGoods);Assert.assertFalse(goods.getGoodsSkuList() == cloneGoods.getGoodsSkuList());}}

运行单测结果如图,通过!

6. 个人感悟/总结

6.1.1 原文摘录

  1. 在多线程环境中,如果我们不通过克隆构造新的对象,线程池中两个线程会公用同一个对象,后面对订单号的修改将影响到其它线程。因此如果能够克隆一个新的对象,并且对新对象的修改不影响原始对象,就能实现我们期待的效果。
  2. 浅拷贝将返回该类的新的实例,该实例的引用类型对象共享。深拷贝也会返回该类的新的实例,但是该实例的引用类型属性也是拷贝的新对象。
  3. 浅拷贝和深拷贝的主要区别在于对于引用类型是否共享。
  4. Never make the client do anything the library can do for the client.—— 不要让客户端去做任何类库可以替它完成的事。
  5. 项目中引用的常见工具包的工具类实现深拷贝,避免重复造轮子

6.1.2 自我感悟

  1. 浅拷贝最外层基本类型只是传值,外层的引用类型仍然指向原始对象。深拷贝是通过序列化等方式整个对象图,即对象的引用关系(包含引用类型属性)都创建为新对象。 实际使用要根据场景来,比如你编写一个函数,参数为一个 List 类型,如果你不使用克隆,那么对这个集合的增删,将影响到上层调用者。因此需要进行拷贝。因为字符串是不可变的,所以浅拷贝就可以满足。 List 这种类型,如果采用浅拷贝,取出里面的cat 修改属性,也会影响到调用方,如果想完全互不影响,可以使用深拷贝。

http://chatgpt.dhexx.cn/article/6x47KeVB.shtml

相关文章

JS 浅拷贝和深拷贝详解(巨详细)

目录 一、前置知识详解 1.1.JavaScript数据类型 1.2.理解传值和传址 二、浅拷贝 2.1.浅拷贝的定义和原理 2.2.实现浅拷贝的方法 2.2.1.手写递归实现 2.2.2.利用展开语法实现浅拷贝 2.2.3.Object.assign进行对象的合并 2.2.4.利用Array.prototype.,slice() 2.2.5.利用…

SV中的浅拷贝和深拷贝

1.浅拷贝 浅拷贝&#xff1a; 只拷贝对象中的数据变量&#xff0c;而对于对象中的数据操作(一般为任务和函数)和其中定义的其他类的句柄&#xff0c;采用类似“引用”的方式&#xff0c;浅拷贝前后共用同一内存空间。 可以使用new操作符进行复制。如果一个类包含指向另一个类的…

什么是浅拷贝和深拷贝?

文章目录 1、什么是浅拷贝&#xff1f;2、什么是深拷贝&#xff1f;2.1、常见的深拷贝方式 在Java语言中&#xff0c;当我们需要拷贝一个Java对象的时候&#xff0c;常见的会有两种方式的拷贝:浅拷贝与深拷贝。 浅拷贝&#xff1a;只是拷贝了源对象的地址&#xff0c;所以源对象…

基于java实现浅拷贝和深拷贝

目录 1、概念2、浅拷贝2.1、浅拷贝实战 3、深拷贝3.1、嵌套 clone 方法3.2、使用序列化流3.3、使用开源工具类 1、概念 浅拷贝&#xff1a;在拷贝一个对象时&#xff0c;复制基本数据类型的成员变量&#xff0c;但对引用数据类型的成员变量只进行引用的传递&#xff08;复制其…

js实现浅拷贝和深拷贝

一.数据类型 数据分为基本数据类型和引用数据类型 基本数据类型(String, Number, Boolean, Null, Undefined&#xff0c;Symbol) 引用数据类型&#xff08;Object[Array属于Object]&#xff09; 基本数据类型的特点&#xff1a;直接存储在栈(stack)中的数据 引用数据类型的特点…

浅拷贝和深拷贝

最近学习string类的时候感觉这里概念有点混淆&#xff0c;浅拷贝与深拷贝的区别&#xff0c;上网查了一下&#xff0c;原来没我想 的难么复杂&#xff0c;以下是我的理解&#xff0c;如果有不对的地方求大佬留言交流交流。 浅拷贝&#xff1a; 顾名思义就是浅层的拷贝&#xff…

理解浅拷贝和深拷贝以及实现方法

一、数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined&#xff0c;Symbol)和引用数据类型Object&#xff0c;包含&#xff08;function&#xff0c;Array&#xff0c;Date&#xff09;。 1、基本数据类型的特点&#xff1a;直接存储在栈内存中的数据 …

VUE浅拷贝和深拷贝

文章目录 前言一、数据类型1.1.基本数据类型1.2.引用数据类型1.3.区别 二、浅拷贝2.1.定义2.2.浅拷贝特点 三、深拷贝3.1.定义3.2.深拷贝特点 四、拷贝实现方案4.1.Object.assign()4.2.concat()4.3.slice()4.4.JSON.parse(JSON.stringify())4.5.cloneDeep() 五、结论 前言 在理…

C# 中的浅拷贝和深拷贝

在本文中&#xff0c;将通过示例讨论C&#xff03;中的浅拷贝和深拷贝。这是上一篇文章的续篇。因此&#xff0c;在继续本文之前&#xff0c;请阅读以前的文章&#xff0c;其中之前使用示例讨论了C&#xff03;中的原型设计模式。 什么是深拷贝和浅拷贝&#xff1f; 浅复制和…

JAVA 浅拷贝和深拷贝

拷贝 拷贝即对已有的数据创建一个副本&#xff0c;在 Java 中&#xff0c;拷贝可分为引用拷贝、浅拷贝、深拷贝。 引用拷贝 在 Java 中&#xff0c;实例化后的对象存储在堆区&#xff0c;而局部变量存放在局部变量表(栈)中&#xff0c;如&#xff1a; public void yinYongC…

js浅拷贝和深拷贝

1、JS数据类型 基本数据类型&#xff1a;Boolean、String、Number、null、undefined 引用数据类型&#xff1a;Object、Array、Function、RegExp、Date等 2、深拷贝与浅拷贝 深拷贝和浅拷贝都只针对引用数据类型&#xff0c; 浅拷贝会对对象逐个成员依次拷贝&#xff0c;但…

C++浅拷贝和深拷贝

1、浅拷贝 浅拷贝&#xff1a;又称值拷贝&#xff0c;将源对象的值拷贝到目标对象中去&#xff0c;本质上来说源对象和目标对象共用一份实体&#xff0c;只是所引用的变量名不同&#xff0c;地址其实还是相同的。 举个简单的例子&#xff0c;你的小名叫西西&#xff0c;大名叫…

彻底理解Python中浅拷贝和深拷贝的区别

目录 前言 1. 浅拷贝和深拷贝的概念 2. is和的区别 3. 赋值操作 4. copy模块里面的copy()方法 5. copy模块里面的deepcopy()方法 6.字典自带的copy方法 7.切片表达式拷贝 8.总结 前言 Python 的所有变量其实都是指向内存中的对象的一个指针&#xff0c;这确实和之前学…

如何理解java的回调函数?

对于技术问题&#xff0c;会用是一回事&#xff0c;理解这个技术问题的来龙去脉、设计者当初为什么要设计这个功能、这个技术问题有哪些优势、适用哪些场景又是另外回事了。 前者照猫画虎得其形&#xff0c;后者形神兼备得其意&#xff0c;这也是所谓青铜与王者的区别。 会使…

java使用回调函数

java回调函数 回调函数&#xff08;callback Function&#xff09;&#xff0c;顾名思义就是用来回调的函数。在两个类A、B中&#xff0c;A在调用B接口的同时B也在调用A 回调函数也常用于线程中的异步获取消息。 举个简单的例子&#xff0c;公司中老板分发任务给员工A&#…

java中回调函数的实现

在java的事务中&#xff0c;有时候可能会遇到以下情况&#xff0c;第一步是更新某某表&#xff0c;中间可能要更新不确定的多步&#xff0c;最后一步是更新缓存&#xff0c;结构大致如下&#xff1a; &#xff08;1&#xff09;updateA(); &#xff08;2&#xff09;updateXX…

什么是java回调函数

回调函数 一&#xff1a;故事背景二&#xff1a;概念三&#xff1a;回调函数的作用四&#xff1a;java中如何进行回调4.1 类图4.2 定义回调接口4.3 实现回调接口4.4 调用方法使用回调函数4.5 Main函数调用4.6 总结描述 五&#xff1a;回调函数的优点5.1 灵活性5.2 解耦性5.3 异…

简单举例JAVA回调函数的实现

来自维基百科的对回调&#xff08;Callback&#xff09;的解释&#xff1a;In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execut…

java回调函数机制

Java回调函数机制 参考了网上的一些资料&#xff0c;下面也做出一些总结&#xff0c;供初学者了解学习。 一、 概述 软件模块之间总是存在着一定的接口&#xff0c;从调用方式上&#xff0c;可以把他们分为三类&#xff1a;同步调用、回调、异步调用 。 同步调用&#xff1a;一…

java中如何实现回调函数

最近工作需要研究了一会别人写的库&#xff0c;其中充满着各种"回调函数"&#xff0c;因此把自己理解给记录下来&#xff0c;存档。 首先我们来看看回调函数 这个概念的具体由来&#xff0c;百度百科的示义如下&#xff1a; 回调函数就是一个通过函数指针调用的函数。…