Java对象克隆

Java对象克隆

Java提供了克隆的机制,Object类有clone方法,用于对象克隆。

clone()和Cloneable接口

Cloneable是一个用于标记的接口,接口没有任何声明方法

clone()方法,是Object类定义的方法(protected),该方法会将对象的所有字段进行复制

如果一个类没有实现Cloneable接口,并且它的实例调用了clone()方法,就会抛出异常

怎样克隆?

  1. 如果一个类允许被克隆,则要实现Cloneable接口。
  2. 覆盖clone()方法。Object中的clone()方法被protected修饰,覆盖时要用public修饰

    class Test implements Cloneable{
        private Date date;
        public Test clone() {
            Test clone = null;
            try {
                clone = (Test) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return clone;
        }
    }   
    

调用clone()方法,就可以得到与母本独立的副本吗?
Object.clone()方法,对象所占内存空间的硬拷贝(直接复制二进制)这样显然是不行的,Object.clone()方法,只是将引用复制了,并没有创建新的Date对象,就导致复制出来的Test对象中的date,和母本指向同一个Date对象(母本.date == 副本.date)。

怎样保证母本和副本独立?(进行“深拷贝”,关于这个概念稍后再说)
引用类型不可以直接赋值

public class Clone {
    public static void main(String[] args) {
        Date d1 = new Date();
        Date d2 = d1;
        System.out.println("修改前--------------------------------");
        System.out.println("d1:" + d1 + ", d2=" + d2);
        System.out.println("只修改d1---------------------");
        d1.setDate(1);
        System.out.println("d1:" + d1 + ", d2=" + d2);
    }
}   

运行结果:
  

对于一个对象来说,如果直接复制引用,就会导致两个引用指向一个对象(在C里,就是两个指针指向同一个对象)。这样导致的结果就是,母本和副本是同一个对象,使用一个引用引起的变化,会导致另一个引用指向的同一个对象发生变化,显然副本和母本是不独立的。

                                         

对于引用类型(可变对象的引用类型),克隆时不能简单的使用直接赋值的方式,而是调用这个对象的clone()方法

class Test implements Cloneable{

    private Date date;

    public Test clone() {
        Test clone = null;
        try {
            clone = (Test) super.clone();

            clone.date = (Date) this.date.clone();

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

对于不可变对象的引用类型,例如String,克隆时可以和基本类型一样,使用直接赋值的方式

“浅拷贝”和“深拷贝”

“浅拷贝”:只是简单的复制,不会考虑副本和母本是否有公用的对象(直接调用Object.clone)。
“深拷贝”:保证副本和母本之间相互独立。

深拷贝覆盖clone()方法的规则:

  1. 首先,调用super.clone()方法。
  2. 对于可变对象的引用类型的字段,通过调用它的clone方法,进行克隆。

解析:

调用super.clone()方法,克隆基本类型和不可变对象的引类型字段;以及克隆父类成员。

如果一个对象可以被修改(任何一个字段),则这个类就是可变的,所以,对于可变的对象,要进行单独的克隆。

举例:

class Test implements Cloneable{

    int a;
    private Date date1;
    private Date date2;

    public Test clone() {
        Test clone = null;
        try {
            clone = (Test) super.clone();

            clone.date1 = (Date) this.date1.clone();
            clone.date2 = (Date) this.date2.clone();

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

关于clone() 方法的注意事项

1.不会执行构造方法(函数)

public class Test implements Cloneable{

    int a;

    public Test(int a) {
        this.a = a;
        System.out.println("执行Test构造方法");
    }
    @Override
    public Test clone() {
        Test clone = null;
        try {
            clone = (Test)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
        return clone;
    }

    public static void main(String[] args) {
        Test t = new Test(1);
        Test t1 = t.clone();
    }
}

                                          

构造方法只执行了一次,对象克隆时,并没有执行构造方法。

2.被final修饰的字段无法“深拷贝”

public class Test implements Cloneable{

    int a;
    final Test b = null;

    @Override
    public Test clone() {
        Test clone = null;
        try {
            clone = (Test)super.clone();

    /**********下面这条语句会报错**********/
            clone.b = this.b.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
        return clone;
    }
}

报错:不能对final字段赋值。

对final字段进行初始化只能在构造方法或声明的时候,初次之外,无法再次进行赋值。

但是,如果不进行“深拷贝”, 也就是删掉报错的那句话,就不会报错,但是这样就是“浅拷贝”,这个字段可以被多个对象修改,很不安全。

发布了213 篇原创文章 · 获赞 116 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq2071114140/article/details/103595295