Java中浅拷贝和深拷贝详解
大家好,本篇文章主要讲的是Java中浅拷贝和深拷贝详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
目录
Java浅拷贝深拷贝
实现浅拷贝
实现深拷贝
Java浅拷贝深拷贝
浅拷贝和深拷贝涉及到了Object类中的clone()方法
实现浅拷贝
浅拷贝的实现需要类重写clone()方法
浅拷贝会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值;
如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象,导致两个对象的引用不等。
实现浅拷贝很简单只需要将类实现Cloneable接口然后重写clone方法即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | class Person implements Cloneable { String name; int age; public Person() { } public Person(String name, int age) { this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public int getAge() { return age; } public void setAge( int age) { this .age = age; } /** * 重写clone()方法 * * @return * @throws CloneNotSupportedException */ @Override protected Object clone() throws CloneNotSupportedException { return super .clone(); } @Override public String toString() { return "Person{" + "name='" + name + '\ '' + ", age=" + age + '}' ; } } |
测试浅拷贝特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void testClone() throws CloneNotSupportedException { Person person1 = new Person(); person1.setName( "ccy" ); person1.setAge( 20 ); Person person2 = (Person) person1.clone(); //查看浅拷贝效果 System.out.println(person1); System.out.println(person2); System.out.println(person1.getName() == person2.getName()); //验证clone()的特性 System.out.println(person1.clone() != person1); System.out.println(person1.clone().getClass() == person1.getClass()); //如果是基本类型浅拷贝直接赋值值,如果是引用类型浅拷贝指向其内存地址即共享内存地址 //改变person1的引用类型String属性的值,引用发生改变 person1.setName( "zfs" ); System.out.println(person2.getName()); } |
实现深拷贝
对于上述的问题虽然拷贝的两个对象不同,但其内部的一些引用还是相同的,怎么样绝对的拷贝这个对象,使这个对象完全独立于原对象呢?就使用我们的深拷贝了。深拷贝:在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量。
在具体实现深拷贝上,这里提供两个方式,重写clone()方法和序列法。
重写clone()方法
如果使用重写clone()方法实现深拷贝,那么要将类中所有自定义引用变量的类也去实现Cloneable接口实现clone()方法。对于字符类可以创建一个新的字符串实现拷贝。但是对于自定义类需要实现cloneable重写clone,这样做就太麻烦了所以我们使用序列化
序列化
序列化后将二进制字节流内容写到一个媒介(文本或字节数组),然后是从这个媒介读取数据,原对象写入这个媒介后拷贝给clone对象,原对象的修改不会影响clone对象,因为clone对象是从这个媒介读取。
熟悉对象缓存的知道我们经常将Java对象缓存到Redis中,然后还可能从Redis中读取生成Java对象,这就用到序列化和反序列化。一般可以将Java对象存储为字节流或者json串然后反序列化成Java对象。因为序列化会储存对象的属性但是不会也无法存储对象在内存中地址相关信息。所以在反序列化成Java对象时候会重新创建所有的引用对象。
在具体实现上,自定义的类需要实现Serializable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | class Person implements Serializable { String name; int age; public Person() { } public Person(String name, int age) { this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public int getAge() { return age; } public void setAge( int age) { this .age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\ '' + ", age=" + age + '}' ; } protected Person 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 (Person) ois.readObject(); } } |
测试方法
1 2 3 4 5 6 7 | public void testClone() throws Exception { Person person1 = new Person(); person1.setName( "ccy" ); person1.setAge( 20 ); Person person2 = person1.deepClone(); System.out.println(person1.getName() == person2.getName()); } |
可以看到两个引用对象的地址并不同,成功实现了深拷贝
到此这篇关于Java中浅拷贝和深拷贝详解的文章就介绍到这了