java之实现Cloneable接口的详解,克隆一个对象--对应有浅克隆和深克隆,概念结合代码深入理解

来源:互联网 发布:对角矩阵的n次方怎么求 编辑:程序博客网 时间:2024/05/17 03:57

1.首先,你要知道怎么实现克隆:实现Cloneable接口,在bean里面重写clone()方法,权限为public。
2.其次,你要大概知道什么是地址传递,什么是值传递。
3.最后,你要知道你为什么使用这个clone方法。

先看第一条,简单的克隆代码的实现。这个也就是我们在没了解清楚这个Java的clone的时候,会出现的问题。
看完代码,我再说明这个时候的问题。
先看我要克隆的学生bean的代码:

package com.lxk.model;/** * 学生类:有2个属性:1,基本属性-String-name;2,引用类型-Car-car。 * <p> * Created by lxk on 2017/3/23 */public class Student implements Cloneable {    private String name;    private Car car;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Car getCar() {        return car;    }    public void setCar(Car car) {        this.car = car;    }    @Override    public String toString() {        return "Student{" +                "name='" + name + '\'' +                ", car=" + car +                '}';    }    @Override    public Student clone() {        Student student = null;        try {            student = (Student) super.clone();        } catch (CloneNotSupportedException ignored) {            System.out.println(ignored.getMessage());        }        return student;    }}

学生内部引用了Car这个bean

package com.lxk.model;import java.util.List;public class Car implements Comparable<Car> {    private String sign;    private int price;    private List<Dog> myDog;    private List<String> boys;    public Car() {    }    public Car(String sign, int price) {        this.sign = sign;        this.price = price;    }    public Car(String sign, int price, List<Dog> myDog) {        this.sign = sign;        this.price = price;        this.myDog = myDog;    }    public Car(String sign, int price, List<Dog> myDog, List<String> boys) {        this.sign = sign;        this.price = price;        this.myDog = myDog;        this.boys = boys;    }    public String getSign() {        return sign;    }    public void setSign(String sign) {        this.sign = sign;    }    public int getPrice() {        return price;    }    public void setPrice(int price) {        this.price = price;    }    public List<Dog> getMyDog() {        return myDog;    }    public void setMyDog(List<Dog> myDog) {        this.myDog = myDog;    }    public List<String> getBoys() {        return boys;    }    public void setBoys(List<String> boys) {        this.boys = boys;    }    @Override    public int compareTo(Car o) {        //同理也可以根据sign属性排序,就不举例啦。        return this.getPrice() - o.getPrice();    }    @Override    public String toString() {        return "Car{" +                "sign='" + sign + '\'' +                ", price=" + price +                ", myDog=" + myDog +                ", boys=" + boys +                '}';    }}


最后就是main测试类
package com.lxk.findBugs;import com.lxk.model.Car;import com.lxk.model.Student;/** * 引用传递也就是地址传递需要注意的地方,引起的bug * <p> * Created by lxk on 2017/3/23 */public class Bug2 {    public static void main(String[] args) {        Student student1 = new Student();        Car car = new Car("oooo", 100);        student1.setCar(car);        student1.setName("lxk");        //克隆完之后,student1和student2应该没关系的,修改student1不影响student2的值,但是完之后发现,你修改car的值,student2也受影响啦。        Student student2 = student1.clone();        System.out.println("学生2:" + student2);//先输出student2刚刚克隆完之后的值,然后在修改student1的相关引用类型的属性值(car)和基本属性值(name)        car.setSign("X5");        student1.setName("xxx");        System.out.println("学生2:" + student2);//再次输出看修改的结果    }}

之后就该是执行的结果图了:



对上面执行结果的疑惑,以及解释说明:

我们可能觉得自己在bean里面实现clone接口,重写了这个clone方法,那么学生2是经由学生1clone,复制出来的,
那么学生1和学生2,应该是毫不相干的,各自是各自,然后,在修改学生1的时候,学生2是不会受影响的。
但是结果,不尽人意。从上图执行结果可以看出来,除了名字,这个属性是没有被学生1影响,关于car的sign属性已经
因为学生1的变化而变化,这不是我希望的结果。
可见,这个简单的克隆实现也仅仅是个“浅克隆”,也就是基本类型数据,他是会给你重新复制一份新的,但是引用类型的,
他就不会重新复制份新的。引用类型包括,上面的其他bean的引用,list集合,等一些引用类型。


那么怎么实现深克隆呢?
对上述代码稍作修改,如下:
学生bean的clone重写方法如下所示:

    @Override    public Student clone() {        Student student = null;        try {            student = (Student) super.clone();            if (car != null) {                student.setCar(car.clone());            }        } catch (CloneNotSupportedException ignored) {            System.out.println(ignored.getMessage());        }        return student;    }


然后还要Car类实现cloneable接口,复写clone方法:

    @Override    public Car clone() {        Car car = null;        try {            car = (Car) super.clone();            if (myDog != null) {                car.setMyDog(Lists.newArrayList(myDog));            }            if (boys != null) {                car.setBoys(Lists.newArrayList(boys));            }        } catch (CloneNotSupportedException ignored) {            System.out.println(ignored.getMessage());        }        return car;    }

主测试代码不动,这个时候的执行结果如下:


可以看到,这个时候,你再修改学生1的值,就不会影响到学生2的值,这才是真正的克隆,也就是所谓的深克隆。


怎么举一反三?
可以看到,这个例子里面的引用类型就一个Car类型的属性,但是实际开发中,除了这个引用其他bean类型的属性外,
可能还要list类型的属性值用的最多。那么要怎么深克隆呢,就像我在Car bean类里面做的那样,把所有的引用类型的属性,
都在clone一遍。那么你在最上层调用这个clone方法的时候,他就是真的深克隆啦。
我代码里面那么判断是为了避免空指针异常。当然,这个你也得注意咯。

注意 重写clone方法的时候,里面各个属性的null的判断哦。

最后,觉得写的好,还请点个赞,留个言啥的。觉得有问题的,提出来,我改,但是,请不要踩我的文章。


2 0
原创粉丝点击