浅谈Java对象拷贝&浅拷贝&深拷贝

对象拷贝:对象拷贝没有生成新对象,对象地址是一样的

Apublic class A implements Cloneable{

    private Integer a;

    private B b;

    public int getA() {
        return a;
    }

    public void setA(Integer a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return "A{" +
                "a=" + a +
                ", b=" + b +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

Bpublic class B {

    private String b;

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }

    @Override
    public String toString() {
        return "B{" +
                "b='" + b + '\'' +
                '}';
    }
}

public static void main(String[] args) throws CloneNotSupportedException {
	// write your code here
        B b=new B();
        b.setB("haha");
        A a=new A();
        a.setA(1);
        a.setB(b);
        System.out.println("对象拷贝前a的值 "+"a: "+a);

        A aa= a;
        aa.setA(2);//修改b而未修改a
        B bb=new B();
        bb.setB("enen");
        aa.setB(bb);
        System.out.println("对象拷贝后a的值 "+"a: "+a);
    }

结果为

对象拷贝前a的值 a: A{a=1, b=B{b='haha'}}
对象拷贝后a的值 a: A{a=2, b=B{b='enen'}}

我们发现对象拷贝并没有创建新的对象,而是引用a与aa都指向同一个地址
在这里插入图片描述
浅拷贝:简明的说,就是浅拷贝会创建一个新的对象,对于基本类型会拷贝值,而对于引用类型会拷贝地址,需要实现Cloneable接口并重写clone()方法

public static void main(String[] args) throws CloneNotSupportedException {
        // write your code here
        B b=new B();
        b.setB("haha");
        A a=new A();
        a.setA(1);
        a.setB(b);
        System.out.println("浅拷贝拷贝前a的值 "+"a: "+a);

        A aa= (A) a.clone();
        aa.setA(2);//修改b而未修改a
        B bb=aa.getB();
        bb.setB("enen");
        aa.setB(bb);
        System.out.println("浅拷贝拷贝后a的值 "+"a: "+a);
    }

结果为

浅拷贝拷贝前a的值 a: A{a=1, b=B{b='haha'}}
浅拷贝拷贝后a的值 a: A{a=1, b=B{b='enen'}}

我们发现浅拷贝确实是会创建一个新的对象,对于基本类型会拷贝值,对于引用类型则会拷贝地址,所以导致修改了aa的B,a的B也会跟着修改

深拷贝:浅拷贝会带来数据安全隐患,所以我们需要深拷贝,深拷贝需要对象里面的所有引用类型都要实现Cloneable接口并重写clone()方法

public class A implements Cloneable{

    private Integer a;

    private B b;

    public int getA() {
        return a;
    }

    public void setA(Integer a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return "A{" +
                "a=" + a +
                ", b=" + b +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        A a= (A) super.clone();
        B bb= (B) b.clone();
        a.setB(bb);
        return a;
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }
}

public class B implements Cloneable {

    private String b;

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }

    @Override
    public String toString() {
        return "B{" +
                "b='" + b + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public static void main(String[] args) throws CloneNotSupportedException {
        // write your code here
        B b=new B();
        b.setB("haha");
        A a=new A();
        a.setA(1);
        a.setB(b);
        System.out.println("深拷贝拷贝前a的值 "+"a: "+a);

        A aa= (A) a.clone();
        aa.setA(2);//修改b而未修改a
        B bb=aa.getB();
        bb.setB("enen");
        aa.setB(bb);
        System.out.println("深拷贝拷贝后a的值 "+"a: "+a);
    }

结果为

深拷贝拷贝前a的值 a: A{a=1, b=B{b='haha'}}
深拷贝拷贝后a的值 a: A{a=1, b=B{b='haha'}}

我们发现深拷贝后,aa的修改并未影响到a的值
Cloneable相关

/**
 * A class implements the <code>Cloneable</code> interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.
 * <p>
 * Invoking Object's clone method on an instance that does not implement the
 * <code>Cloneable</code> interface results in the exception
 * <code>CloneNotSupportedException</code> being thrown.
 * <p>
 * By convention, classes that implement this interface should override
 * <tt>Object.clone</tt> (which is protected) with a public method.
 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.
 * <p>
 * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.
 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}

这个接口的说明我们去看文档

public interface Cloneable一个类实现Cloneable接口,以指示Object.clone()方法,该方法对于该类的实例进行现场复制是合法的。 
在不实现Cloneable接口的实例上调用对象的克隆方法导致抛出异常CloneNotSupportedException 。 

按照惯例,实现此接口的类应使用公共方法覆盖Object.clone (受保护)。 有关覆盖此方法的详细信息,请参阅Object.clone() 。 

注意,此接口不包含clone方法。 因此,只能通过实现该接口的事实来克隆对象是不可能的。 即使克隆方法被反射地调用,也不能保证它成功。 

从以下版本开始: 
JDK1.0 
另请参见: 
CloneNotSupportedExceptionObject.clone() 

要想用Object.clone()方法就得实现Cloneable接口,否则会报CloneNotSupportedException
反例如下:

Exception in thread "main" java.lang.CloneNotSupportedException: com.company.wh.A
	at java.lang.Object.clone(Native Method)
	at com.company.wh.A.clone(A.java:24)
	at com.company.wh.Main.main(Main.java:11)

Object.clone()的文档里也很清楚的说明了这一点

protected Object clone()
                throws CloneNotSupportedException创建并返回此对象的副本。 “复制”的精确含义可能取决于对象的类。 一般的意图是,对于任何对象x ,表达式: 
 x.clone() != x将是真实的,而且表达: 
 x.clone().getClass() == x.getClass()将是true ,但这些都不是绝对的要求。 通常情况是: 
 x.clone().equals(x)将是true ,这不是一个绝对的要求。 
按照惯例,返回的对象应该通过调用super.clone获得。 如果一个类和它的所有超类(除了Object )遵守这个惯例,那将是x.clone().getClass() == x.getClass()的情况。 

按照惯例,此方法返回的对象应该与此对象(正被克隆)无关。 为了实现这一独立性,可能需要修改super.clone返回的对象的一个或多个字段。 通常,这意味着复制构成被克隆的对象的内部“深层结构”的任何可变对象,并通过引用该副本替换对这些对象的引用。 如果一个类仅包含原始字段或对不可变对象的引用,则通常情况下, super.clone返回的对象中的字段通常不需要修改。 

clone的方法Object执行特定的克隆操作。 首先,如果此对象的类不实现接口Cloneable ,则抛出CloneNotSupportedException 。 请注意,所有数组都被认为是实现接口Cloneable ,并且数组类型T[]的clone方法的返回类型是T[] ,其中T是任何引用或原始类型。 否则,该方法将创建该对象的类的新实例,并将其所有字段初始化为完全符合该对象的相应字段的内容,就像通过赋值一样。 这些字段的内容本身不被克隆。 因此,该方法执行该对象的“浅拷贝”,而不是“深度拷贝”操作。 

Object类本身并不实现接口Cloneable ,因此在类别为Object的对象上调用clone方法将导致运行时抛出异常。 

结果 
这个实例的一个克隆。 
异常 
CloneNotSupportedException - 如果对象的类不支持Cloneable接口。 覆盖clone方法的子类也可以抛出此异常以指示实例无法克隆。 
另请参见: 
Cloneable

本来想看Object.clone()的源码,但发现是调用的本地方法

protected native Object clone() throws CloneNotSupportedException;

所以这个就结束了。。。

补充:
一直在思考,Integer,String等其实都是引用类型,也没见他们实现Cloneable接口,重写clone()方法,他们是怎么实现深浅拷贝的呢?
看到一个最好的答案,就是String,Integer等是Immutable Object,每一次赋值,指向的都是堆上的一个新对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值