对象的克隆clone

克隆,也叫复制,包括:深克隆、浅克隆(深复制或浅复制)。java的clone()方法定义在Object类中,克隆方法将对象复制一份返回给调用者。

一般而言:

1、对任意的对X象,都有X.clone() != X;

克隆对象与原对象不是一个对象

2、对任何对象X,都有X.getClass == X.clone().getClass();

克隆对象与原对象的类型一样

3、如果X的equals()方法定义恰当,那么X.clone().equals(X)应该成立。

浅复制

被复制的所有变量都与原来对象中的变量有相同的值,而所有的对其他对象的引用仍指向原来的对象。如图:


深复制

被复制的所有变量都与原来对象中的变量有相同的值,而所有的对其他对象的引用指向原来的对象所复制的一份新对象。如图:


克隆的准备条件:

1、要被克隆的对象必须implements Cloneable;

2、复写Object类中的clone()方法,Object类中的clone()方法是protected类型的,在复写时要改成public类型的;

3、在复写的clone()方法中,第一行写super.clone();原因如下:

通过调用super.clone();在运行时,Object类的clone()方法会自动识别出要复制的是哪个对象,然后为此对象分配空间,将原始对象中的内容一一复制到新对象的存储空间中。

注意:继承Object类中的clone()方法是浅复制。


深克隆、浅克隆示例

【浅克隆】

package com.lpp.clone;

public class CloneDemo
{
    public static void main(String[] args) throws Exception
    {
        Children c = new Children();
        c.setAge(16);
        c.setName("children1");
        
        Person p = new Person();
        p.setAge(28);
        p.setName("lpp");
        p.setChildren(c);
        
        Person p2 = (Person)p.clone();
        //打印新对象中的内容
        System.out.println(p2.getName()+":"+p2.getAge());
        System.out.println(p2.getChildren().getName()+":"+p2.getChildren().getAge());
        
        System.out.println("--------------------");
        
        p2.getChildren().setName("children2");//修改新对象中的Children引用
        p2.getChildren().setAge(17);
        //打印原对象中的内容
        System.out.println(p.getName()+":"+p.getAge());
        System.out.println(p.getChildren().getName()+":"+p.getChildren().getAge());
        //打印新对象中修改了Children的内容
        System.out.println(p2.getName()+":"+p2.getAge());
        System.out.println(p2.getChildren().getName()+":"+p2.getChildren().getAge());
    }
}
class Person implements Cloneable//实现Cloneable接口
{
    int age;
    String name;
    Children children;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public Children getChildren()
    {
        return children;
    }
    public void setChildren(Children children)
    {
        this.children = children;
    }
    @Override
    public Object clone() throws CloneNotSupportedException//访问权限修改成public
    {
        return super.clone();
    }
    
}
class Children
{
    int age;
    String name;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    
}

结果:

lpp:28
children1:16
--------------------
lpp:28
children2:17
lpp:28
children2:17

可见,修改了新对象中的Chiledren引用所指向的内容,原对象和新对象中Children的内容都会改变,这就证实了浅克隆中,对象中对其他对象的引用所指向的内容没有被复制,始终只有一份。


【深克隆】

对上述代码做如下改变:

class Person implements Cloneable//实现Cloneable接口
{
    int age;
    String name;
    Children children;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public Children getChildren()
    {
        return children;
    }
    public void setChildren(Children children)
    {
        this.children = children;
    }
    @Override
    public Object clone() throws CloneNotSupportedException//访问权限修改成public
    {
        Person p = (Person)super.clone();
        p.setChildren((Children)(p.getChildren().clone()));//克隆p中的Children
        return p;
    }
    
}
class Children implements Cloneable//实现Cloneable接口
{
    int age;
    String name;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    @Override
    public Object clone() throws CloneNotSupportedException//访问权限修改成public
    {
        return super.clone();
    }
}

结果:

lpp:28
children1:16
--------------------
lpp:28
children1:16
lpp:28
children2:17

可知,克隆了Person中的Children后,Person中的Children引用指向一个新的Children,因此修改新的Children,不会对原来的Children有影响。


另一种实现深克隆的方法为将要克隆的内容进行序列化,然后再通过反序列化,将缓存中的内容读回来,应该强调的是写在流里的是原对象的一个拷贝,原对象还保存在JVM里面。这样做的前提是对象及所引用的对象都是可序列化的,否则应考虑将不可序列化的对象设为用transien,将其排除在序列化之外。

利用序列化实现深克隆

package com.lpp.clone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SerializableClone
{
    public static void main(String[] args) throws Exception
    {
        Student s = new Student();
        s.setAge(12);
        s.setName("student1");
        
        Teacher t = new Teacher();
        t.setAge(28);
        t.setName("lpp");
        t.setStudent(s);
        
        Teacher t2 = (Teacher)t.deepClone();
        //打印新对象中的内容
        System.out.println(t2.getName()+":"+t2.getAge());
        System.out.println(t2.getStudent().getName()+":"+t2.getStudent().getAge());
        
        System.out.println("--------------------");
        
        t2.getStudent().setName("student2");//修改新对象中的Children引用
        t2.getStudent().setAge(13);
        //打印原对象中的内容
        System.out.println(t.getName()+":"+t.getAge());
        System.out.println(t.getStudent().getName()+":"+t.getStudent().getAge());
        //打印新对象中修改了Children的内容
        System.out.println(t2.getName()+":"+t2.getAge());
        System.out.println(t2.getStudent().getName()+":"+t2.getStudent().getAge());
    }
}
class Teacher implements Serializable//实现Serializable接口而不是Cloneable接口
{
    int age;
    String name;
    Student student;
    
    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public Student getStudent()
    {
        return student;
    }

    public void setStudent(Student student)
    {
        this.student = student;
    }

    public Object deepClone() throws Exception
    {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);//把当前对象的拷贝写到流里,进行序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();//从当前流里读取对象拷贝,进行反序列化
        
    }
    
}
class Student implements Serializable//实现Serializable接口而不是Cloneable接口
{
    int age;
    String name;
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    
}

结果:

lpp:28
student1:12
--------------------
lpp:28
student1:12
lpp:28
student2:13

可见通过序列化,实现了对象的深度拷贝。这是实现深克隆的最好的办法。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值