Java-Clone

Java Clone

什么是Clone

创建并返回此对象的副本。
默认情况下,Java克隆是逐字段复制,因为Object类不了解调用clone()的类结构。

“复制”的确切含义可能取决于对象的类别:

  1. 如果该类仅具有原始数据类型成员,则将创建该对象的全新副本,并返回对该新对象副本的引用。
  2. 如果类包含任何类类型的成员,则仅复制对那些成员的对象引用,因此原始对象和克隆对象中的成员引用都引用同一对象。
    除了上述Object实现的clone行为外,你始终可以覆盖clone()并指定你的行为。

实现Clone:

在Java中,如果一个类需要支持克隆,则它必须执行以下操作:

  1. 您必须实现Cloneable接口。
  2. 您必须重写clone()Object类中的方法。[有点奇怪。clone()方法应该已经在Cloneable接口中。]
    有关clone()的Java文档如下:
/*
Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/

第一条语句保证克隆的对象将具有单独的内存地址分配。
第二条语句建议原始对象和克隆对象应具有相同的类类型,但这不是强制性的。
第三条语句建议使用equals()方法使原始对象和克隆对象相等,但这不是强制性的。

举个列子:

public class Employee implements Cloneable{
 
    private int empoyeeId;
    private String employeeName;
    private Department department;
 
    public Employee(int id, String name, Department dept)
    {
        this.empoyeeId = id;
        this.employeeName = name;
        this.department = dept;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
     
    //Getters and Setters
}


public class Department
{
    private int id;
    private String name;
 
    public Department(int id, String name)
    {
        this.id = id;
        this.name = name;
    }
 
    //Getters and Setters
}

public class TestCloning 
{
 
    public static void main(String[] args) throws CloneNotSupportedException
    {
        Department dept = new Department(1, "Human Resource");
        Employee original = new Employee(1, "Admin", dept);
 
        //Lets create a clone of original object
        Employee cloned = (Employee) original.clone();
 
        //Let verify using employee id, if cloning actually workded
        System.out.println(cloned.getEmpoyeeId());
 
        //Verify JDK's rules
 
        //Must be true and objects must have different memory addresses
        System.out.println(original != cloned);
 
        //As we are returning same class; so it should be true
        System.out.println(original.getClass() == cloned.getClass());
 
        //Default equals method checks for references so it should be false. If we want to make it true,
        //then we need to override equals method in Employee class.
        System.out.println(original.equals(cloned));
    }
}
 
Output:
 
1
true
true
false

太好了,我们成功地克隆了Employee对象。但是,请记住,我们有两个对同一个对象的引用,现在它们都将更改应用程序不同部分中对象的状态。想看看如何?让我们来看看。

        Department hr = new Department(1, "Human Resource");
        Employee original = new Employee(1, "Admin", hr);
        Employee cloned = (Employee) original.clone();
 
        //Let change the department name in cloned object and we will verify in original object
        cloned.getDepartment().setName("Finance");
 
        System.out.println(original.getDepartment().getName());
        System.out.println(cloned.getDepartment().getName());

---
Output:
 
Finance
Finance

可以看到,当我改变原始对象的属性时,克隆对象的属性也被修改了。这是因为是我们复制的只是Employee对象的引用,当我们通过引用去修改对象的属性时,指向这对象的引用获得的对象属性肯定也是被修改的。

浅拷贝

深拷贝

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

clone(),只能对当前对象进行浅拷贝,引用指向的依然是同一个对象。那么如何进行深拷贝呢?
常见的两种方案:

  1. 序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。
  2. 重写clone()方法:自行实现引用类型属性的复制方式。

例子:

//Modified clone() method in Employee class
@Override
protected Object clone() throws CloneNotSupportedException {
    Employee cloned = (Employee)super.clone();
    cloned.setDepartment((Department)cloned.getDepartment().clone());   
    return cloned;
}

Department.java
//Defined clone method in Department class.
@Override
protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}

TestDeepClone.java
public class TestCloning 
{
    public static void main(String[] args) throws CloneNotSupportedException 
    {
        Department hr = new Department(1, "Human Resource");
 
        Employee original = new Employee(1, "Admin", hr);
        Employee cloned = (Employee) original.clone();
 
        //Let change the department name in cloned object and we will verify in original object
        cloned.getDepartment().setName("Finance");
 
        System.out.println(original.getDepartment().getName());
        System.out.println(cloned.getDepartment().getName());
    }
}
 
Output:
 
Human Resource
Finance

可以看到更改克隆对象属性时,不会影响原始对象。

因此,深度克隆需要满足以下规则-

  1. 无需单独复制基元。

  2. 原始类中的所有成员类均应支持克隆,而上下文中原始类的clone方法应调用super.clone()所有成员类。

  3. 如果任何成员类不支持克隆,则必须在克隆方法中创建该成员类的新实例,并将其所有属性一一复制到新的成员类对象中。这个新的成员类对象将在克隆对象中设置。.

  4. No need to separately copy primitives.

  5. All the member classes in original class should support cloning and in clone method of original class in context should call super.clone() on all member classes.

  6. If any member class does not support cloning then in clone method, one must create a new instance of that member class and copy all its attributes one by one to new member class object. This new member class object will be set in cloned object.

参考文章:
https://howtodoinjava.com/java/cloning/a-guide-to-object-cloning-in-java/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值