Moosphan/Android-Daily-Interview

2019-06-13:Java 中深拷贝与浅拷贝的区别?

Moosphan opened this issue · 14 comments

2019-06-13:Java 中深拷贝与浅拷贝的区别?

深拷贝是创建一个新的对象然后赋值相同的值,浅拷贝是引用指向同一个对象

深拷贝是址拷贝,浅拷贝是值拷贝

深拷贝是址拷贝,浅拷贝是值拷贝

你说反了吧

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址。因此如果其中一个对象改变了地址,就会影响到另一个对象。
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢而且花销大。
如何选择:
如果对象的属性全是基本类型的,那么可以使用浅拷贝,但是如果对象有引用属性,那就要基于具体的需求来选择浅拷贝还是深拷贝。我的意思是如果对象引用任何时候都不会被改变,那么没必要使用深拷贝,只需要使用浅拷贝就行了。如果对象引用经常改变,那么就要使用深拷贝。没有一成不变的规则,一切都取决于具体需求。

首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。那先来看看浅拷贝和深拷贝的概念。在 Java 中,除了基本数据类型(元类型)之外,还存在 类的实例对象 这个引用数据类型。而一般使用 『 = 』号做赋值操作的时候。对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际上还是指向的同一个对象。而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。所以到现在,就应该了解了,所谓的浅拷贝和深拷贝,只是在拷贝对象的时候,对 类的实例对象 这种引用数据类型的不同操作而已。总结来说:1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

在Java中浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。
所以到现在,就应该了解了,所谓的浅拷贝和深拷贝,只是在拷贝对象的时候,对 类的实例对象 这种引用数据类型的不同操作而已。
总结来说:
1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。并且它们之间的hashcode不一样,只是其数据类型字段相同。
2. 深拷贝:是对对象,数据类型值and 它们之间的hashcode拷贝;进行对象完完全全的clone();原对象与新对象对比完全相同。

无论是拷贝基本数据类型还是引用类型,只要没有创建一个新的对象就成为浅拷贝,如果创建了一个对象并且复制了其内的成员变量成为深拷贝

看了一会 还是不明白 和头像一样 打卡打卡

浅拷贝和深拷贝的区别是 如果对象里的成员变量存在引用类型, 当使用浅拷贝时, 新对象的这个属性的地址值和源对象的这个属性一致,即浅拷贝只是对引用类型的地址值拷贝,深拷贝是创建了新的对象,只是属性复制, 浅拷贝的缺点是如果对对象进行多处复制,如果在运行中对其中一个对象的属性进行改变, 那么所有复制的对象都会被改变,不利于管理

浅拷贝:
1.实现Cloneable
2.成员变量是基本数据类型,也包括String类型

public class ShallowCopy implements Cloneable {
private int age;
private String name;

public ShallowCopy(int age, String name) {
    this.age = age;
    this.name = name;
}

@Override
public Object clone() {
    ShallowCopy s = null;
    try {
        s = (ShallowCopy) super.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return s;
}

@Override
public String toString() {
    return "{" +
            "age=" + age +
            ", name='" + name + '\'' +
            '}';
}

public static void main(String[] args) {

    ShallowCopy sample = new ShallowCopy(1, "wislie");

    ShallowCopy cloneObj = (ShallowCopy) sample.clone();

    System.out.println("浅度拷贝 (cloneObj == sample):" + (cloneObj == sample)); //false
    System.out.println("sample:" + sample); //sample:{age=1, name='wislie'}
    System.out.println("cloneObj:" + cloneObj); //cloneObj:{age=1, name='wislie'}
}

}

深拷贝:
有两种实现方式,一种是实现Cloneable,成员变量需要有引用类型的对象;另一种实现了Serializable,对成员变量没有限制.
实现Cloneable的方式:
public class DeepCopy implements Cloneable {

private String value;
private ShallowCopy shaderCopy;

public DeepCopy(String value, ShallowCopy shaderCopy) {
    this.value = value;
    this.shaderCopy = shaderCopy;
}

public String getValue() {
    return value;
}

public void setValue(String value) {
    this.value = value;
}

public ShallowCopy getShaderCopy() {
    return shaderCopy;
}

public void setShaderCopy(ShallowCopy shaderCopy) {
    this.shaderCopy = shaderCopy;
}

@Override
protected Object clone() {
    DeepCopy d = null;
    try {
        d = (DeepCopy) super.clone();
        if(shaderCopy != null){
            d.setShaderCopy((ShallowCopy) shaderCopy.clone());
        }
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return d;
}

@Override
public String toString() {
    return "{" +
            "value='" + value + '\'' +
            ", shaderCopy=" + shaderCopy +
            '}';
}

public static void main(String[] args) {
    DeepCopy sample = new DeepCopy("high", new ShallowCopy(12, "wislie"));
    DeepCopy cloneObj = (DeepCopy) sample.clone();
    System.out.println("深度拷贝 (cloneObj == sample):" + (cloneObj == sample)); //false
    System.out.println("sample:" + sample); //{value='high', shaderCopy={age=12, name='wislie'}}
    System.out.println("cloneObj:" + cloneObj);//{value='high', shaderCopy={age=12, name='wislie'}}
}

}
实现Serializable的方式
public class DeepCopy2 implements Serializable {

private static final long serialVersionUID = 369285298572961L;  //最好是显式声明ID
private int len;
private Inner inner;

public DeepCopy2(int len, Inner inner) {
    this.len = len;
    this.inner = inner;
}

static class Inner implements Serializable {
    private static final long serialVersionUID = 369285298572941L;  //最好是显式声明ID

    private String color;
    private int alpha;

    public Inner(String color, int alpha) {
        this.color = color;
        this.alpha = alpha;
    }

    @Override
    public String toString() {
        return "Inner{" +
                "color='" + color + '\'' +
                ", alpha=" + alpha +
                '}';
    }
}

@Override
public String toString() {
    return "DeepCopy2{" +
            "len=" + len +
            ", inner=" + inner +
            '}';
}

private DeepCopy2 deepClone() {
    DeepCopy2 data = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(this);
        //将流序列化成对象
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        data = (DeepCopy2) ois.readObject();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return data;
}

public static void main(String[] args) {
    DeepCopy2 sample = new DeepCopy2(10, new Inner("blue", 255));
    DeepCopy2 copySample = sample.deepClone();

    System.out.println("sample==copySample:" + (sample == copySample)); //false
    System.out.println("sample:" + sample); //DeepCopy2{len=10, inner=Inner{color='blue', alpha=255}}
    System.out.println("copySample:" + copySample); //DeepCopy2{len=10, inner=Inner{color='blue', alpha=255}}
}

}
不管是浅拷贝还是深拷贝,对象的地址都变了;如果我说的有问题,欢迎指正

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址。因此如果其中一个对象改变了地址,就会影响到另一个对象。
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢而且花销大。
如何选择:
如果对象的属性全是基本类型的,那么可以使用浅拷贝,但是如果对象有引用属性,那就要基于具体的需求来选择浅拷贝还是深拷贝。我的意思是如果对象引用任何时候都不会被改变,那么没必要使用深拷贝,只需要使用浅拷贝就行了。如果对象引用经常改变,那么就要使用深拷贝。没有一成不变的规则,一切都取决于具体需求。

确定会创建一个新对象吗,感觉深拷贝才会创建啊

深拷贝中,如果拷贝的是引用类型,那么会创建一个新的引用类型,并把这个引用类型的地址赋值给新的对象。但是如果这个引用类型指向的对象后续还包括引用类型属性的话,后面会继续深拷贝吗?

没有什么问题是一个例子理解不了的
https://blog.csdn.net/qwildwolf/article/details/120138500

浅拷贝:
java多数场景使用的都是浅拷贝方式,像是集合、函数参数等都是使用的浅拷贝,两个对象指向相同的内存地址,对对象内容操作,所有共同引用的对象都可见。
深拷贝:
需要独立实现,拷贝出来的对象和原对象没有任何关联,两者的操作也互不影响。