对象拷贝(克隆)

1.为什么要使用克隆?

根据已有对象创建一个与已有对象完全相同的新对象,并且对新对象所做的任何修改不会影响到已有对象,此时就需要用到克隆。也就是说,如果想对某个对象进行修改,同时还要保留该对象的原有数据,以便后期对原有数据进行操作,就需要使用克隆。

如果通过new关键字创建一个新的对象,将原有对象所有属性值赋值给新对象对应的属性值,也可以实现克隆的效果。但是对于属性特别多的对象,需要反复使用get和set方法,特别繁琐。另外,clone()是Object类中的native方法,执行克隆操作的速度也是很快的。

protected native Object clone() throws CloneNotSupportedException;

2.如何实现对象克隆?

对象克隆的四种方式:

1.通过new关键字和get、set方法实现对象的克隆;

2.通过java.lang.Object类中clone方法实现对象的克隆(浅克隆和深克隆);

3.通过工具类BeanUtils或PropertyUtils的copyProperties方法实现对象的克隆;

4.通过序列化实现对象的克隆。

这里只对第二种克隆对象的方式进行介绍。通过clone方法实现对象的克隆,必须满足以下条件:

1.对象的类必须实现Cloneable接口;

2.对象的类必须覆盖Object类的clone()方法(访问修饰符设为public,默认是protected);

3.在clone()方法中调用super.clone();

上面三个条件只能实现浅克隆。如果对象中存在引用类型的变量(String类型的除外,因为String是final类,是不可变类),若想实现深克隆,则引用类型变量对应的实体类也必须满足上面的条件。

3.深拷贝和浅拷贝区别是什么?

浅拷贝:对外层对象和对象中基本类型的变量进行拷贝。若对象中有引用类型变量,即对象中有内层对象,则浅拷贝只能复制内层对象的地址值,浅拷贝完成之后,原对象与新对象拥有同一个内层对象。

深拷贝:对外层对象和对象中基本类型变量以及引用类型变量都可以拷贝,即深拷贝既能复制外层对象,也能复制内层对象。深拷贝完成之后,原对象与新对象拥有不同的内层对象。

浅拷贝

示例代码:

//学生类(对学生对象进行拷贝,实现Cloneable接口)
public class Student implements Cloneable{
    private String name;//引用类型(String类没有实现Cloneable接口)
    private int age;//基本类型
    private Classes classes;//引用类型
    //这里省略getters and setters
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", classes=" + classes + "]";
    }
    //覆盖java.lang.Object类中的clone方法(注意:这里实现的是浅克隆)
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student stu = (Student)super.clone();  //调用Object类的clone方法
        return stu;
    }   
}
//班级类Classes(没有实现Cloneable接口)
public class Classes{
    private int classId;//基本类型
    private String className;//引用类型
    //这里省略getters and setters
    @Override
    public String toString() {
        return "Classes [classId=" + classId + ", className=" + className + "]";
    }
}
//测试类
public class TestClone {
    public static void main(String[] args) {
        //创建一个Student类的实例stu
        Student stu = new Student();
        stu.setName("张三");
        stu.setAge(10);
        Classes classes = new Classes();
        classes.setClassId(101);
        classes.setClassName("一班");
        stu.setClasses(classes);
        try {
            System.out.println("浅克隆测试------");
            //对stu实例克隆得到stu2实例
            Student stu2 = (Student)stu.clone();
            System.out.println("两个对象是否相同:" + (stu == stu2));
            System.out.println("两个对象的name属性是否相同:" + (stu.getName() == stu2.getName()));
            System.out.println("两个对象的classes属性是否相同:" + (stu.getClasses() == stu2.getClasses()));
            System.out.println("浅克隆,Stu:" + stu);
            System.out.println("浅克隆,Stu2:" + stu);
            System.out.println("修改克隆对象属性");
            //修改克隆实例stu2的属性(浅克隆,会修改原对象的属性)
            stu2.setName("李四");//修改姓名
            stu2.setAge(20);//修改年龄
            stu2.getClasses().setClassId(102);//修改班级编号
            stu2.getClasses().setClassName("二班");//修改班级名称
            System.out.println("修改克隆对象属性后,Stu:" + stu);
            System.out.println("修改克隆对象属性后,Stu2:" + stu2);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

测试结果:

浅克隆测试------
两个对象是否相同:false
两个对象的name属性是否相同:true
两个对象的classes属性是否相同:true
浅克隆,Stu:Student [name=张三, age=10, classes=Classes [classId=101, className=一班]]
浅克隆,Stu2:Student [name=张三, age=10, classes=Classes [classId=101, className=一班]]
修改克隆对象属性
修改克隆对象属性后,Stu:Student [name=张三, age=10, classes=Classes [classId=102, className=二班]]
修改克隆对象属性后,Stu2:Student [name=李四, age=20, classes=Classes [classId=102, className=二班]]

由测试结果可以发现原始对象stu和克隆对象stu2不是同一对象,但是两个对象的name属性和classes属性是同一个对象,并且原始对象stu和克隆对象stu2的属性值是相等的,这也验证了“Java的克隆机制是对类的实例的属性逐一复制”。name属性和classes同为引用类型的实例,克隆后原始对象stu和克隆对象stu2的name属性和classes属性是同一对象,说明没有实现Cloneable接口的类(如此处的String和Classes)的实例克隆是通过传引用实现的,即内层对象没有克隆。

当执行stu2.setName("李四")修改stu2对象的name属性时,在常量池中创建一个"李四"对象,并让stu2的name属性指向该对象,但是stu的name属性依然指向"张三"对象,这是因为String是不可变类,修改的只是String类型变量的引用。

深克隆

//学生类(对学生对象进行拷贝,实现Cloneable接口)
public class Student implements Cloneable{
    private String name;//引用类型
    private int age;//基本类型
    private Classes classes;//引用类型
    //这里省略gettters and setters
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", classes=" + classes + "]";
    }
    //覆盖java.lang.Object类中的clone方法(注意:这里实现的是深克隆)
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student stu = (Student)super.clone();
        //克隆classes属性时调用Classes类的clone(),对内层对象也要克隆
        Classes cla = (Classes)classes.clone();
        stu.setClasses(cla);
        return stu;
    }   
}
//Classes类实现Cloneable接口,对内层对象进行克隆,实现深克隆
public class Classes implements Cloneable {
    private int classId;//基本类型
    private String className;//引用类型
    //这里省略gettters and setters
    @Override
    public String toString() {
        return "Classes [classId=" + classId + ", className=" + className + "]";
    }

    //重写clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
//测试类
public class TestClone {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.setName("张三");
        stu.setAge(10);
        Classes classes = new Classes();
        classes.setClassId(101);
        classes.setClassName("一班");
        stu.setClasses(classes);
        try {
            System.out.println("深克隆测试------");
            //对stu克隆得到stu2实例
            Student stu2 = (Student)stu.clone();
            System.out.println("两个对象是否相同:" + (stu == stu2));
            System.out.println("两个对象的name属性是否相同:" + (stu.getName() == stu2.getName()));
            System.out.println("两个对象的classes属性是否相同:" + (stu.getClasses() == stu2.getClasses()));
            System.out.println("深克隆,Stu " + stu);
            System.out.println("深克隆,Stu2 " + stu);
            System.out.println("修改克隆对象属性");
            //修改克隆实例stu2的属性(深克隆,不会修改原对象的属性)
            stu2.setName("李四");//修改姓名
            stu2.setAge(20);//修改年龄
            stu2.getClasses().setClassId(102);//修改班级编号
            stu2.getClasses().setClassName("二班");//修改班级名称
            System.out.println("修改克隆对象属性后,Stu " + stu);
            System.out.println("修改克隆对象属性后,Stu2 " + stu2);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

测试结果:

深克隆测试------
两个对象是否相同:false
两个对象的name属性是否相同:true
两个对象的classes属性是否相同:false
深克隆,Stu Student [name=张三, age=10, classes=Classes [classId=101, className=一班]]
深克隆,Stu2 Student [name=张三, age=10, classes=Classes [classId=101, className=一班]]
修改克隆对象属性
修改克隆对象属性后,Stu Student [name=张三, age=10, classes=Classes [classId=101, className=一班]]
修改克隆对象属性后,Stu2 Student [name=李四, age=20, classes=Classes [classId=102, className=二班]]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值