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的判断哦。
上面的是override clone()方法来实现深克隆的。如果你这个要克隆的对象很复杂的话,你就不得不去每个引用到的对象去复写这个clone方法,这个太啰嗦来,改的地方,太多啦。
还有个方法就是使用序列化来实现这个深拷贝
/** * 对象的深度克隆,此处的对象涉及Collection接口和Map接口下对象的深度克隆 * 利用序列化和反序列化的方式进行深度克隆对象 * * @param object 待克隆的对象 * @param <T> 待克隆对象的数据类型 * @return 已经深度克隆过的对象 */ public static <T extends Serializable> T deepCloneObject(T object) { T deepClone = null; ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; ByteArrayInputStream bais = null; ObjectInputStream ois = null; try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(object); bais = new ByteArrayInputStream(baos .toByteArray()); ois = new ObjectInputStream(bais); deepClone = (T)ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { try { if(baos != null) { baos.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(oos != null) { oos.close(); } } catch (IOException e) { e.printStackTrace(); } try{ if(bais != null) { bais.close(); } } catch (IOException e) { e.printStackTrace(); } try{ if(ois != null) { ois.close(); } } catch (IOException e) { e.printStackTrace(); } } return deepClone; }
具体的使用如下:
/** * 使用序列化来实现深拷贝简单。但是,所涉及到的所有对象都的实现序列化接口。 */ private static void cloneBySerializable() { Student student1 = new Student(); Car car = new Car("oooo", 100, Lists.newArrayList(new Dog("aaa", true, true))); student1.setCar(car); student1.setName("lxk"); Student student2 = deepCloneObject(student1); System.out.println("学生2:" + student2); car.setSign("X5"); car.setMyDog(null); student1.setName("xxx"); System.out.println("学生2:" + student2); }
实现的效果,还是和上面的一样的,但是这个就简单多来,只需要给涉及到的每个引用类型,都去实现序列化接口就好啦。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对小牛知识库的支持。如果你想了解更多相关内容请查看下面相关链接
本文向大家介绍深拷贝与 浅拷贝的区别?相关面试题,主要包含被问及深拷贝与 浅拷贝的区别?时的应答技巧和注意事项,需要的朋友参考一下 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
本文向大家介绍Python浅拷贝与深拷贝用法实例,包括了Python浅拷贝与深拷贝用法实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Python浅拷贝与深拷贝用法。分享给大家供大家参考。具体分析如下: 浅拷贝,创造了新的对象,但是它的对象引用确和被拷贝对象一致。 如果被拷贝对象的构成元素是不可变类型,则拷贝对象对这个元素的改变,实际上是创造一个新的对象。 如果被拷贝对象的构成元素是可
本文向大家介绍深拷贝浅拷贝的区别和实现?相关面试题,主要包含被问及深拷贝浅拷贝的区别和实现?时的应答技巧和注意事项,需要的朋友参考一下 参考回答: 数组的浅拷贝: 如果是数组,我们可以利用数组的一些方法,比如slice,concat方法返回一个新数组的特性来实现拷贝,但假如数组嵌套了对象或者数组的话,使用concat方法克隆并不完整,如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或数
主要内容:到底是浅拷贝还是深拷贝对于基本类型的数据以及简单的对象,它们之间的拷贝非常简单,就是按位复制内存。例如: b 和 obj2 都是以拷贝的方式初始化的,具体来说,就是将 a 和 obj1 所在内存中的数据按照二进制位(Bit)复制到 b 和 obj2 所在的内存, 这种默认的拷贝行为就是 浅拷贝 ,这和调用 memcpy() 函数的效果非常类似。 对于简单的类,默认的拷贝构造函数一般就够用了,我们也没有必要再显式地定义一
浅拷贝 对于对象或数组类型,当我们将a赋值给b,然后更改b中的属性,a也会随着变化。 也就是说,a和b指向了同一块堆内存,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝。 深拷贝 那么相应的,如果给b放到新的内存中,将a的各个属性都复制到新内存里,就是深拷贝。 也就是说,当b中的属性有变化的时候,a内的属性不会发生变化。 参考链接: 深拷贝与浅拷贝的实现(一) javaScript中浅拷
一、引言 对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部数据。Java中有三种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)、延迟拷贝(Lazy Copy)。 二、浅拷贝 1、什么是浅拷贝 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着