【Java进阶五】对象序列化和拷贝

Java拷贝问题
Java中有个接口叫做Cloneable,实现该接口的类都具有克隆(被拷贝)的能力。拷贝的话在性能方面会比我们直接new一个对象的性能要好,特别是在大对象的生成上,性能提升非常明显。原因是拷贝是在内存中进行浅拷贝。

浅拷贝
浅拷贝的基本规则如下:

  1. 基本类型
    如果变量是基本,则拷贝变量的值,比如:int、float等
  2. 对象
    如果变量是一个实例对象,则拷贝其地址的引用。也就是说拷贝之后,新对象和原来对象的引用会指向堆里面同一个实例
  3. String字符串
    若变量为String字符串,也是拷贝其地址引用。但是和第2点不同的是,在修改时,会从字符串池当中重新生成一个字符串,原有对象持有的字符串不变话。

    通过上面所说的三点,我们知道,如果一个对象被拷贝后,新对象对自己持有对象进行改变时,会改变原有对象的实例。拷贝只是拷贝了基本数据类型的值,对象属性却只是拷贝了引用并没有拷贝实例,这种就是浅拷贝。相对于深拷贝,深拷贝就是在拷贝对象属性引用的同时,还拷贝了引用所指的实例。对于深的拷贝而言,性能不一定比new高,假如一个对象持有100个引用对象,那么深拷贝就是要拷贝100+1次,引用对象还持有引用对象不算。而new一个新的对象只需要1次,然后进行设值。

利用序列化进行对象的拷贝
在内存中通过字节流的拷贝来实现序列化拷贝。把原来对象写入到字节流中,在从字节流中将其读出,这样就新产生了一个新的对象,并且这个对象与原来对象不存在属性的引用共享问题,从而实现了深拷贝。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;


public class CloneUtils {

    @SuppressWarnings("unchecked")
    public static extends Serializable> T clone(T obj) {
        T cloneObj = null;
        try {
            // 写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();

            // 分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            // 返回生成的新对象
            cloneObj = (T)ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

这是一个序列化拷贝的例子。但需要注意两点:

  1. 对象的内部属性都是可序列化的
    如果对象的内部属性不可序列化,则会抛出序列化异常

  2. 注意方法和属性的特殊修饰符
    比如final、static变量的序列化问题会被引导对象拷贝中来。同时transient瞬态变量也是无法序列化的。

序列化拷贝有一个简单的方法,就是引入apache包下的SerializationUtil类。

你可能感兴趣的:(Java进阶)