java对象克隆

前言:前几天在做题的时候,其中我定义一个数组arr1,将结果值存入该数组,然后因为需要在数组中查找,为了提高查询效率,需要将该数组的元素进行排序,但是还需要保留原顺序的数组,于是高能来了,直接int[] arr2=arr1;将arr2排序完之后,再进行接下来的运算...结果发现测试数据结果不对,debug环境下看,结果arr1顺序不是原来的顺序了,恍然大悟犯了一个低级错误,int[] arr2=arr1这种方式是将arr1的引用赋值给arr2,arr1和arr2指向内存堆的同一个对象,如果去对arr2进行改变,那么它在内存堆中指向的数组对象会改变,那么arr1的值也会做同样的改变。然后我在思考怎么样能让它们互相不干扰?脑海中立马浮现“对象克隆”四个字。

首先思考一个问题:为什么要克隆?直接new一个对象不行吗?

就像我做题的那个问题,直接new的一个对象值还是初始化的值,那如果将对象属性一个一个赋值给新对象呢?比如遍历数组arr1,这种方法可以,但是通过看clone方法的代码,发现它是一个native方法,是最底层的方法,速度快效率高。

还原我那个低级错误,如下:

        int[] a={1,2,3};
        int[] b=a;
        System.out.println("改变值之前:");
        System.out.println("a:"+Arrays.toString(a)+","+"b:"+Arrays.toString(b));
        a[0]=10;
        System.out.println("改变值之后:");
        System.out.println("a:"+Arrays.toString(a)+","+"b:"+Arrays.toString(b));

运行结果如下:

改变值之前:
a:[1, 2, 3],b:[1, 2, 3]
改变值之后:
a:[10, 2, 3],b:[10, 2, 3]

因为数组a和b指向同一个对象,好比一个房间只有两把钥匙,a拿着钥匙开门去房间把柜子打翻了然后关门走了,那么b再进门的时候,柜子仍然是翻的;

那么我们使用克隆试试,如下:

        int[] a={1,2,3};
        int[] b=a.clone();
        System.out.println("改变值之前:");
        System.out.println("a:"+Arrays.toString(a)+","+"b:"+Arrays.toString(b));
        a[0]=10;
        System.out.println("改变值之后:");
        System.out.println("a:"+Arrays.toString(a)+","+"b:"+Arrays.toString(b));

运行结果如下:

改变值之前:
a:[1, 2, 3],b:[1, 2, 3]
改变值之后:
a:[10, 2, 3],b:[1, 2, 3]

这就好比b买了一个和a一模一样的房间,而它们分别对房间里的事物做任何操作,是互相不影响的。 

如何实现克隆?

有两种克隆方法,一是浅克隆,一是深克隆。在java中,数据类型分为值类型和引用类型,浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。

举一个例子

class Student implements Cloneable {
    private int age;

    public int getAge() {
        return this.age;
    }

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

    @Override
    public Object clone() {
        Student stu = null;
        try {
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

}

public class DemoClone {
    public static void main(String[] args) {
        Student stu1 = new Student();
        stu1.setAge(18);
        Student stu2 = (Student) stu1.clone();
        System.out.println(stu1 == stu2);
        System.out.println("克隆之后stu2的age:"+stu2.getAge());
        stu1.setAge(15);
        System.out.println("改变stu1的age之后stu2的age:"+stu2.getAge());
    }
}

运行结果为:

false
克隆之后stu2的age:18
改变stu1的age之后stu2的age:18

 以上称为浅克隆;还有一种深度复制,就是对象成员变量是引用数据类型,比如我们在Student类里添加一个自定义的对象成员;

class Dog{
    private int money;
    public int getMoney() {
        return this.money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}
class Student implements Cloneable {
    private int age;
    private Dog dog;
    public int getAge() {
        return this.age;
    }

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

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public Object clone() {
        Student stu = null;
        try {
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }

}

public class DemoClone {
    public static void main(String[] args) {
        Student stu1 = new Student();
        Dog d1=new Dog();
        d1.setMoney(100);
        stu1.setAge(18);
        stu1.setDog(d1);
        Student stu2 = (Student) stu1.clone();
        System.out.println(stu1 == stu2);
        System.out.println("stu1:"+stu1.getAge()+","+stu1.getDog().getMoney());
        System.out.println("stu2:"+stu2.getAge()+","+stu2.getDog().getMoney());
        stu1.getDog().setMoney(50);
        System.out.println("stu1:"+stu1.getAge()+","+stu1.getDog().getMoney());
        System.out.println("stu2:"+stu2.getAge()+","+stu2.getDog().getMoney());
        System.out.println(stu1.getDog()==stu2.getDog());
    }
}

运行结果如下:

false
stu1:18,100
stu2:18,100
stu1:18,50
stu2:18,50
true

改变stu1的dog属性,stu2的dog属性也跟着变了,原因是浅克隆只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。

所以,为了达到真正的克隆对象,而不是纯粹引用复制。我们需要将Dog类实现Cloneable接口,并且重写clone方法,完整代码如下:

class Dog implements Cloneable {
    private int money;

    public int getMoney() {
        return this.money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    @Override
    public Object clone() {
        Dog dog = null;
        try {
            dog = (Dog) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return dog;
    }
}

class Student implements Cloneable {
    private int age;
    private Dog dog;

    public int getAge() {
        return this.age;
    }

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

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public Object clone() {
        Student stu = null;
        try {
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        stu.dog = (Dog)dog.clone();//<---深度复制
        return stu;
    }
}

public class DemoClone {
    public static void main(String[] args) {
        Student stu1 = new Student();
        Dog d1 = new Dog();
        d1.setMoney(100);
        stu1.setAge(18);
        stu1.setDog(d1);
        Student stu2 = (Student) stu1.clone();
        System.out.println(stu1 == stu2);
        System.out.println("stu1:" + stu1.getAge() + "," + stu1.getDog().getMoney());
        System.out.println("stu2:" + stu2.getAge() + "," + stu2.getDog().getMoney());
        stu1.getDog().setMoney(50);
        System.out.println("stu1:" + stu1.getAge() + "," + stu1.getDog().getMoney());
        System.out.println("stu2:" + stu2.getAge() + "," + stu2.getDog().getMoney());
        System.out.println(stu1.getDog() == stu2.getDog());
    }
}

运行结果如下:

false
stu1:18,100
stu2:18,100
stu1:18,50
stu2:18,100
false

浅克隆和深克隆

1、浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。

简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制

2、深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。

如何实现深克隆?

通过覆盖Object类的clone()方法实现;通过序列化(Serialization)等方式来实现。

如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。

序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

END

參考資料 大佬博客:https://www.cnblogs.com/fnlingnzb-learner/p/10649509.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值