近况
最近挺忙的,忙着找房子,忙着看网课,时间还是很紧迫的,留给开发中学习的时间不太多,
公司中还需要学习项目上的业务等内容,当然,即使再忙,总要回顾一下曾经的日子,总要记录一点东西的习惯
是不曾改变的,复盘自己的过去可以帮助自己的未来更好的做选择。
Java的深浅拷贝
我之前看过相关的博客,我以前也懂得什么是深浅拷贝,但学习不应该停止在这个阶段,当我真正使用的时候
我却不知道如何更好的实现深浅拷贝,或者说有没有别人已经造好的,更优雅的方式实现呢?
这驱使我写下这篇文章,用来记录一下这部分内容。
含义
引用拷贝
引用拷贝就是对象之间直接使用=
进行赋值,这并不会产生新的对象,
只是复制了对象的地址而已,两个变量指向的还是同一个对象。浅拷贝
浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话
浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。深拷贝
深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
深浅拷贝实现方式
浅拷贝实现方式
浅拷贝一般是需要实现cloneable接口,重写clone方法,在方法中调用父类的clone方法返回即可@Data public class Person implements Cloneable{ public String pname; public int page; public Address address; public Person() {} public Person(String pname,int page){ this.pname = pname; this.page = page; this.address = new Address(); } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public void setAddress(String provices,String city ){ address.setAddress(provices, city); } } public class Address { private String provices; private String city; public void setAddress(String provices,String city){ this.provices = provices; this.city = city; } } /* Test 这个p2就是p1浅拷贝的对象,p2对象与p1并不相同, 但p2和p1的address属性却是引用的同一个address对象 */ Person p1 = new Person("zhangsan",21); p1.setAddress("湖北省", "武汉市"); Person p2 = (Person) p1.clone();
当然,你也可以使用别人已经写好的工具类去得到浅拷贝对象
例如spring的BeanUtils,apache的commons中的BeanUtils,hutools的BeanUtil
它们的copyProperties方法都能实现对象的浅拷贝。深拷贝实现方式
实现深拷贝对象有三种方式一种仍然是使用cloneable,即被引用的属性,如Address同样实现
cloneable接口,重写clone方法,下面是案例改造:@Data public class Person implements Cloneable{ public String pname; public int page; public Address address; public Person() {} public Person(String pname,int page,String provices,String city){ this.pname = pname; this.page = page; this.address = new Address(provices,city); } @Override protected Object clone() throws CloneNotSupportedException { Person person = (Person)super.clone(); //对引用属性克隆,使得两个person对象的address引用对象也不相同,实现深拷贝 person.address = (Address)address.clone(); return person; } } @Data public class Address implements Cloneable { private String provices; private String city; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
由上述案例可知,想要实现对象的深拷贝,就需要被拷贝对象及其引用属性都实现cloneable接口并重写clone方法
这种实现深拷贝的缺点是,如果被拷贝对象的引用属性的属性仍然是个引用属性,这个对象仍然需要
实现cloneable接口,这种多层都需要克隆的方式非常不美观,每一个对象都需要重写clone方法不优雅那么另一种实现对象深拷贝的方式是序列化,序列化的实现方式要比cloneable方式要好上不少
要想实现对象深拷贝,需要每一个对象都实现Serializable 接口,下面代码是对上述案例的改造:@Data public class Person implements Serializable { private static final long serialVersionUID = 369285298572941L; public String pname; public int page; public Address address; public Person() {} public Person(String pname,int page,String provices,String city){ this.pname = pname; this.page = page; this.address = new Address(provices,city); } public Object clone(){ Person person = null; try { /* 将该对象序列化成流,因为写在流里的是对象的一个拷贝, 而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝 */ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); // 将流序列化成对象 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); person = (Person) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return person; } } @Data public class Address implements Serializable { private static final long serialVersionUID = 872390113109L; private String provices; private String city; }
什么?这种深拷贝还是很麻烦?只要你的对象,对象的引用属性对象实现了Serializable接口,
你可以使用hutools的ObjectUtil.cloneByStream(obj)
方式,同样可以完成深拷贝。最后一种是使用第三方Json工具类实现对象的深拷贝,这个可能是最优雅的方式了
下面举例使用hutools中的JSONUtil工具类实现对象的深拷贝,其他的jackson,fastson,gson等都有类似的方法@Data public class Person{ public String pname; public int page; public Address address; public Person() {} public Person(String pname,int page,String provices,String city){ this.pname = pname; this.page = page; this.address = new Address(provices,city); } } @Data @AllArgsConstructor public class Address { private String provices; private String city; @Override public String toString() { return "Address [provices=" + provices + ", city=" + city + "]"; } } //Test public class Test { public static void main(String[] args) { Person p = new Person("万一", 24,"浙江","杭州"); String jsonStr = JSONUtil.toJsonStr(p); Person person = JSONUtil.toBean(jsonStr, Person.class); person.address.setProvices("江苏"); person.address.setCity("苏州"); System.out.println(p==person); System.out.println(p); System.out.println(person); System.out.println(p.address==person.address); } } //输出结果,可以发现,已经实现对象的深拷贝,两个address对象并不相等 false Person(pname=万一, page=24, address=Address [provices=浙江, city=杭州]) Person(pname=万一, page=24, address=Address [provices=江苏, city=苏州]) false
文章推荐