Java中的拷贝(简述c++深浅拷贝)

10 篇文章 0 订阅

深浅拷贝

在写Java时会遇到一种情况:如下

int[] primes = {1, 4, 2, 5};
int[] nums = primes;

我们在修改nums时primes也会跟着改变,这就涉及到深浅拷贝的问题。

Java中存在两种拷贝(这里以数组拷贝为例):深拷贝和浅拷贝,理解java的内存开辟原理就非常容易理解深浅拷贝的区别,深拷贝就是另开辟一块存储空间,浅拷贝共享存储。
关键:考虑是基本数据类型还是引用数据类型(对象)

深拷贝:拷贝后数组不存在关联,修改一方都不会引起另一方的改变
浅拷贝:修改一方后另一方也随之改变

那么来看一下深浅拷贝都是怎么使用的,本文主要讲解四种方式:

一、for循环

非常容易理解,就是首先实例化一个与原数组大小相同的数组,然后通过循环将原数组赋值给拷贝数组(深拷贝)。
注意:当数组元素是引用类型时是浅拷贝

二、arraycopy

这个是系统提供的拷贝方式,也是我们推荐使用的拷贝方式,它是浅拷贝,也就是说对于非基本类型而言,它拷贝的是对象的引用,而不是去新建一个新的对象。通过它的代码我们可以看到,这个方法不是用java语言写的,而是底层用c或者c++实现的,因而速度会比较快。

当数组中存放的元素为基本数据类型时,此时发生的是深拷贝;
System.arraycopy(src, srcPos dest, destPos, length);
其中各个参数分别表示 如下:
src :源数组
srcPos:源数组要复制的起始位置
dest:目标数组
destPos:目标数组复制的起始位置
length:复制的长度

 public static void main(String[] args) {
        int[] A = {1,2,3,4,5};
        int[] B = new int[A.length];
        //System.arraycopy(A,0,B,0,A.length);
        System.arraycopy(A,1,B,2,2);
        System.out.println("A : " + Arrays.toString(A));  //A : [1, 2, 3, 4, 5]
        System.out.println("B : " + Arrays.toString(B));  //B : [0, 0, 2, 3, 0]
        System.out.println("===========修改后===========");
        A[0] = 100;
        System.out.println("A : " + Arrays.toString(A));  //A : [100, 2, 3, 4, 5]
        System.out.println("B : " + Arrays.toString(B));  //B : [0, 0, 2, 3, 0]
    }

当元素是引用类型时为浅拷贝。

public static native void arraycopy
	(Object src, int srcPos,Object dest, int destPos,int length);

通过源代码我们可以看到,关键字native说明它不是用java语言写的,而是调用其他语言的代码。

1、先来看看基本数据类型的System.arraycopy() 方法拷贝:
 1 import java.util.Arrays;
 2 public class TestDemo {
 3     public static void main(String[] args) {
 4  
 5         int[] array1 = new int[]{1,2,8,7,6};
 6         int[] array2 = new int[array1.length];
 7         System.arraycopy(array1, 0, array2, 0, array1.length);
 8  
 9         System.out.println("array1 = " + Arrays.toString(array1));
10         System.out.println("array2 = " + Arrays.toString(array2));
11         System.out.println("=========================");
12  
13         array2[0] = 100;
14         System.out.println("array1 = " + Arrays.toString(array1));
15         System.out.println("array2 = " + Arrays.toString(array2));
16  
17     }
18 }

这段程序的结果是:

array1 = [1, 2, 8, 7, 6]
array2 = [1, 2, 8, 7, 6]
=========================
array1 = [1, 2, 8, 7, 6]
array2 = [100, 2, 8, 7, 6]

由结果可以看出,当对复制数组的某个元素进行改变时,并不影响被复制数组对应元素,即对于基本数据类型来说System.arraycopy() 方法是深拷贝。

2、同样的,看一下当对象不是基本数据类型,而是引用数据类型时的情况

看以下例子:

 1 class TestArray{
 2     private int val = 10;
 3     public void setVal(int val){
 4         this.val = val;
 5     }
 6     public int getVal(){
 7         return this.val;
 8     }
 9 }
10  
11 public class TestDemo {
12     /**数组输出方法 */
13     public static void printArray(TestArray[] array){
14         for(int i = 0;i < array.length;i++){
15             System.out.print(array[i].getVal()+" ");
16         }
17         System.out.println();
18     }
19  
20     public static void main(String[] args) {
21         TestArray[] array1 = new TestArray[3];
22         // 数组引用赋值
23         for (int i = 0; i < array1.length; i++){
24             array1[i] = new TestArray();
25         }
26  
27         TestArray[] array2 = new TestArray[array1.length];
28         // 数组System.arraycopy()方法复制
29         System.arraycopy(array1,0,array2,0,array1.length);
30  
31         printArray(array1);
32         printArray(array2);
33         System.out.println("==========");
34  
35         array2[0].setVal(100);;
36         printArray(array1);
37         printArray(array2);
38     }
39 }

这段程序的结果是:

10 10 10 
10 10 10 
==========
100 10 10 
100 10 10

由结果可以看出,当对复制数组的某个元素进行改变时,被复制数组对应元素也随之改变,即对于引用数据类型来说 System.arraycopy() 方法是浅拷贝。

三、copyOf/copyOfRange

这个方法也是浅拷贝,为什么呢?我们看一下它的源代码就知道了。

1 public static byte[] copyOfRange(byte[] original, int from, int to) {
2     int newLength = to - from;
3     if (newLength < 0)
4         throw new IllegalArgumentException(from + " > " + to);
5     byte[] copy = new byte[newLength];
6     System.arraycopy(original, from, copy, 0,Math.min(original.length - from, newLength));
7     return copy;
8 }

可以看到其实Arrays.copyOf()方法在底层是调用了 System.arraycopy() 方法来实现复制,即可以把Arrays.copyOf() 方法看作是 System.arraycopy() 方法的衍生方法,故它的执行机理与 System.arraycopy() 方法相同。 所以 Arrays.copyOf() 方法对于基本数据类型来说是深拷贝,对引用类型来说是浅拷贝。

Arrays.copyOf(原数组,自定义新数组长度);
Arrays.copyOfRange(原数组,from,to);
注意拷贝截取的范围是左闭右开的[from,to)

public static void main(String[] args) {
		int[] A = {1,2,3,4,5};
        int[] B = Arrays.copyOf(A,A.length);
        int[] C = Arrays.copyOfRange(A,1,3);
        System.out.println("A : " + Arrays.toString(A));  //A : [1, 2, 3, 4, 5]
        System.out.println("B : " + Arrays.toString(B));  //B : [1, 2, 3, 4, 5]
        System.out.println("C : " + Arrays.toString(C));  //C : [2, 3]
        System.out.println("===========修改后===========");
        A[0] = 100;
        System.out.println("A : " + Arrays.toString(A));  //A : [100, 2, 3, 4, 5]
        System.out.println("B : " + Arrays.toString(B));  //B : [1, 2, 3, 4, 5]
        System.out.println("C : " + Arrays.toString(C));  //C : [2, 3]
        }

当元素是引用类型时为浅拷贝。

四、clone

同样当数据元素为基本类型时为深拷贝,当是引用类型时为浅拷贝
java.lang.Object.clone()的分析可以参考另一篇文章

public static void main(String[] args) {
        int[] A = {1,2,3,4,5};
        int[] B = A.clone();
        System.out.println("A : " + Arrays.toString(A));  //A : [1, 2, 3, 4, 5]
        System.out.println("B : " + Arrays.toString(B));  //B : [1, 2, 3, 4, 5]
        System.out.println("===========修改后===========");
        A[0] = 100;
        System.out.println("A : " + Arrays.toString(A));  //A : [100, 2, 3, 4, 5]
        System.out.println("B : " + Arrays.toString(B));  //B : [1, 2, 3, 4, 5]
    }

五、总结

拷贝方式数值类型引用类型推荐使用
for循环深拷贝浅拷贝
copyOf深拷贝浅拷贝
arraycopy深拷贝浅拷贝
clone深拷贝浅拷贝

六、c++中的深浅拷贝

在c++中浅拷贝(可以使用默认的拷贝构造函数)就是直接将原来的对象所在内存拷贝一份放到新开辟的内存当中,其内容完全一样(包括指针),所以会在改变当前指针指向的内容后影响原来对象的指针内容。
而深拷贝(初始化拷贝和赋值拷贝)(需要自己声明定义拷贝构造函数,赋值时需要重载赋值运算符)会在拷贝后将指针指向新的内存地址,这样就不会影响原来的对象。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值