Java 对象序列化

Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。
但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。

使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。
必须注意地是,对象序列化保存的是对象的”状态”,即它的成员变量。由此可知,对象序列化不会关注类中的静态变量

Demo:

public static void main(String[] args) throws Exception {
    // 对象序列化输出
    ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream("F:\\1.txt"));
    Man kole = new Man();
    kole.setName("kole");
    objOut.writeObject(kole);
    objOut.close();

    // 对象序列化输入
    ObjectInputStream objIn = new ObjectInputStream(new FileInputStream("F:\\1.txt"));
    Object newMan = objIn.readObject();
    if (newMan instanceof Man){
        System.out.println(((Man)newMan).getName());
    }
    objIn.close();
}
// 可被序列化的对象
public class Man implements Serializable{
    private String name;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

为什么一个类要实现Serializable接口?

// ObjectOutputStream源码
//..
if (obj instanceof String) {
    writeString((String) obj, unshared);
} else if (cl.isArray()) {
    writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
    writeEnum((Enum) obj, desc, unshared);
} else if (obj instanceof Serializable) {
    writeOrdinaryObject(obj, desc, unshared);
} else {
    // 非String、数组、枚举、序列化对象直接报错
    if (extendedDebugInfo) {
        throw new NotSerializableException(
            cl.getName() + "\n" + debugInfoStack.toString());
    } else {
        throw new NotSerializableException(cl.getName());
    }
}
//..

transient

当某个字段被声明为transient后,默认序列化机制就会忽略该字段。

public class Man implements Serializable
{
    // ..
    // 此字段将不会被序列化
    private transient double money;
    // ..
}

writeObject()&readObject()

声明为transitive的字段不一定无法将其序列化, 可通过writeObject()与readObject()方法实现自序列化而跳过transitive关键字;

public class Man implements Serializable{
    private String name;
    private transient double money;

    // 只要添加writeObject和readObject方法即可
    private void writeObject(ObjectOutputStream out) throws IOException {  
        out.defaultWriteObject();  
        out.writeDouble(this.money);  
    }  

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {  
        in.defaultReadObject();  
        this.money = in.readDouble();  
    }
    // get()、set()...
}

why?

// 如果自己实现writeObject; 就根据自己的方法实现
// 因此自己的方法中需要实现默认writeObject的方法。
if (slotDesc.hasWriteObjectMethod()) {
    PutFieldImpl oldPut = curPut;
    curPut = null;
    SerialCallbackContext oldContext = curContext;

    if (extendedDebugInfo) {
        debugInfoStack.push(
            "custom writeObject data (class \"" +
            slotDesc.getName() + "\")");
    }
    try {
        curContext = new SerialCallbackContext(obj, slotDesc);
        bout.setBlockDataMode(true);
        // 调用自定义的writeObject方法
        slotDesc.invokeWriteObject(obj, this);
        bout.setBlockDataMode(false);
        bout.writeByte(TC_ENDBLOCKDATA);
    } finally {
        curContext.setUsed();
        curContext = oldContext;
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }

    curPut = oldPut;
} else {
    // 调用默认的写方法
    defaultWriteFields(obj, slotDesc);
}

Externalizable

无论是使用transient关键字,还是使用writeObject()和readObject()方法,其实都是基于Serializable接口的序列化
JDK中提供了另一个序列化接口–Externalizable

实现该接口之后,之前基于Serializable接口的序列化机制就将失效

 public class Man implements Externalizable {
    private String name;

    private transient double money;
    // 需要构造函数(没有报错no valid constructor)
    public Man(){
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeDouble(this.money);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        this.money = in.readDouble();
    }
}
// Externalizable 继承于Serializable
public interface Externalizable extends java.io.Serializable

// ..

// 如果实现Externalizable则走自定义的方法
if (desc.isExternalizable() && !desc.isProxy()) {
    // 自定义序列化
    // 内部调用writeExternal(this);
    writeExternalData((Externalizable) obj);
} else {
    // 系统实现的序列化
    writeSerialData(obj, desc);
}

readResolve()

当序列化的对象是单例模式时, 可通过readResolve方法返回一个对象, 而替换序列化读取的对象。

public class Man implements Serializable {
    private static Man singleThiz = null;
    private String name;
    private transient double money;

    private Man() {
    }

    // 实现读取结果方法
    private Object readResolve() throws ObjectStreamException {
        return Man.getInstance();
    }
    public static Man getInstance() {
        if (Man.singleThiz == null) {
            synchronized (Man.class) {
                if (Man.singleThiz == null) {
                    Man.singleThiz = new Man();
                }
            }
        }
        return Man.singleThiz;
    }
}
// 是否存在ReadResolveMethod方法
if (obj != null &&
    handles.lookupException(passHandle) == null &&
    desc.hasReadResolveMethod()){
    // 获得readResolve得到的方法
    Object rep = desc.invokeReadResolve(obj);
    if (unshared && rep.getClass().isArray()) {
        rep = cloneArray(rep);
    }
    // 生成的obj对象与readResolve方法返回不同,进行替换
    if (rep != obj) {
        handles.setObject(passHandle, obj = rep);
    }
}

无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值