基于序列化的深复制

xiaoxiao2021-02-28  139

复制对象 or 复制引用

在java中,下面代码常常见到

Person p = new Person("cx"); Person p1 = p; System.out.println(p); System.out.println(p1);

执行Person p1 = p指令后真正能达到我们期望的效果吗?

Person@549f9afb Person@549f9afb

可以看到两个输出对象的地址都是一样的,所以这样的写法我们一定需要注意。

java中的clone

在java中使用clone必须要遵守以下几点。

1.在派生类中覆盖基类的clone()方法,并声明为public。(Object类中的clone()方法是protected的)。在子类重写的时候,可以扩大访问修饰符的范围。

2..在派生类的clone()方法中,调用super.clone()。因为在运行时刻,Object类中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。

3.在派生类中实现Cloneable接口。

Person类如下:

public class Person implements Cloneable{ private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Object clone() throws CloneNotSupportedException { return (Person)super.clone(); } }

然后就可以使用clone方法了,代码如下:

Person p = new Person("cx"); Person p1 = (Person) p.clone(); System.out.println(p); System.out.println(p1);

这样的话输出就是2个不同的对象了

@3a780024 @1c515979

不过Object.clone()方法只是浅复制,如果两个Person对象的name的地址值相同, 说明两个对象的name都指向同一个String对象, 也就是浅拷贝, 而如果两个对象的name的地址值不同, 那么就说明指向不同的String对象, 也就是在拷贝Person对象的时候, 同时拷贝了name引用的String对象, 也就是深拷贝。验证代码如下:

Person p = new Person("cx"); Person p1 = (Person) p.clone(); System.out.println(p.getName()==p1.getName());

运行结果为:

true

java序列化与反序列化

Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。

1)JDK类库中序列化API

java.io.ObjectOutputStream:表示对象输出流

它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

java.io.ObjectInputStream:表示对象输入流

它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回。

2)实现序列化的要求

只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常

基于序列化的深复制

将一个对象转化为字节数组,然后在将字节数组转为对象,这2个对象为是同一个对象吗?当然不是,想一下,如果将一个对象序列化后传输到另一个应用中,然后另一个应该此字节数组反序列化后会是一个对象吗?这2个对象都不在同一个jvm中,肯定不会是同一个对象了。同理,将对象转为了字节数组放入输出流中,原对象的地址还是会存在jvm中的,然后从流中读出字节数组,转为为新的对象。其实最主要的方式是ObjectInputStream.readObject(),这个方法读取流转为对象的时候会根据从输入流中解析出来的ObjectStreamClass再构造一个新的ObjectStreamClass对象,在构造方法里边会查找本地(找不到就构造)一个本地对象的描述信息。在创建本地对象描述信息对象的时候,会递归创建超类的描述信息对象。所以新的对象是递归newInstance出来的。

Person类如下:

public class Person implements Serializable{ private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

序列化实现clone的代码如下:

public class CloneUtils { public static <T extends Serializable> T clone(T obj) { T clonedObj = null; ObjectInputStream ois = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ois = new ObjectInputStream(bais); clonedObj = (T) ois.readObject(); ois.close(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != ois) { ois.close(); } } catch (IOException e) { e.printStackTrace(); } } return clonedObj; } }

测试代码如下:

Person p = new Person("cx"); Person p1 = CloneUtils.clone(p); System.out.println(p1.getName()==p.getName());

测试结果如下:

false
转载请注明原文地址: https://www.6miu.com/read-42555.html

最新回复(0)