基于序列化的深复制

来源:互联网 发布:2016淘宝刷单技巧 编辑:程序博客网 时间:2024/05/18 03:57

复制对象 or 复制引用

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

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

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

Person@549f9afbPerson@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
0 0
原创粉丝点击