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

    技术2022-07-11  100

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

    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,每一次赋值,指向的都是堆上的一个新对象

    转载请注明原文地址:https://ipadbbs.8miu.com/read-14042.html
    最新回复(0)