java序列化过程简析

2017-02-08 16:34:33

文章通过一个简单的小栗子,简单分析了java序列化的过程

一个小栗子

public class User implements Serializable{
    private static final long serialVersionUID = -8327620862083656937L;


    private String name;

    // getter and setter

}

序列化

public void serialize() {
    User user = new User();
    user.setName("bin");
    FileOutputStream fileOut = new  FileOutputStream("user.ser");
    ObjectOutputStream out = new ObjectOutputStream(fileOut);
    out.writeObject(user);
    out.close();
    fileOut.close();
}

可以看到相对目录下生成了user.ser文件,内容如下

序列化

简单记录一下该栗子中ObjectOutputStream生成文件的过程。

public ObjectOutputStream(OutputStream out) ... {
    ...
    writeStreamHeader();    // 
}

protected void writeStreamHeader() ... {
    bout.writeShort(STREAM_MAGIC);    // 魔法数0xac ed
    bout.writeShort(STREAM_VERSION);    // 版本0x00 05
}

FileOutputStream.writeObject

public final void writeObject(Object obj)... {
    ...
    writeObject0(obj, false);
}

private void writeObject0(Object obj, boolean unshared) {
    ...
    ObjectStreamClass desc;
    desc = ObjectStreamClass.lookup(cl, true);

    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) { // 序列化对象必须实现Serializable接口
        writeOrdinaryObject(obj, desc, unshared);
    } else {
        ...
    }
}

ObjectStreamClass用于存储一些Class解析结果,如是否继承了Serializable, 是否为enum,字段信息等。ObjectStreamClass.lookup通过反射获取到这些方法,存储在ObjectStreamClass中。

private void writeOrdinaryObject(Object obj,ObjectStreamClass desc,boolean unshared) {
    ...
    bout.writeByte(TC_OBJECT);    // new object标志:0x73            
    writeClassDesc(desc, false); // 记录class描述

    writeSerialData(obj, desc);    // 记录字段内容
}

记录class描述

private void writeClassDesc(ObjectStreamClass desc, boolean unshared) ... {
    int handle;
    if (desc == null) {
        writeNull();
    } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
        writeHandle(handle);
    } else if (desc.isProxy()) {
        writeProxyDesc(desc, unshared);
    } else {
        writeNonProxyDesc(desc, unshared);
    }
}

private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) {
    bout.writeByte(TC_CLASSDESC);    // new Class Descriptor:0x72
    ...
    writeClassDescriptor(desc);    // class描述信息

    bout.writeByte(TC_ENDBLOCKDATA); // object结束标志:0x78

    writeClassDesc(desc.getSuperDesc(), false);    // 记录父类描述: 0x70

}

protected void writeClassDescriptor(ObjectStreamClass desc)
    throws IOException
{
    desc.writeNonProxy(this);
}

void writeNonProxy(ObjectOutputStream out) {
    // 记录class name:serialize.User (000E73657269616C697A652E55736572)
    out.writeUTF(name);    
    // 记录VersionUID(8C6E58F2935F1317)
    out.writeLong(getSerialVersionUID());    

    byte flags = 0;
    ... 
    out.writeByte(flags);    // 相关标志:0x02
    out.writeShort(fields.length);    // 0x0001

    // 记录所有字段的描述
    for (int i = 0; i < fields.length; i++) {
            ObjectStreamField f = fields[i];
            out.writeByte(f.getTypeCode());    // L:4C
            // 字段名name:0004 6E 61 6D 65(0004是字节个数)
            out.writeUTF(f.getName());    
            if (!f.isPrimitive()) {
                // 字段描述Ljava/lang/String;
                //  74 0012 4C6A6176612F6C616E672F537472696E673B
                // 0x74为string标志 0x0012为字节个数
                out.writeTypeString(f.getTypeString());    
            }
        }

}

记录字段内容

private void writeSerialData(Object obj, ObjectStreamClass desc) {
    if (slotDesc.hasWriteObjectMethod()) {
        slotDesc.invokeWriteObject(obj, this);
    } else {
        defaultWriteFields(obj, slotDesc);
    }    
}

private void defaultWriteFields(Object obj, ObjectStreamClass desc) {
    desc.getPrimFieldValues(obj, primVals);    // 原始数据,如int,double
    bout.write(primVals, 0, primDataSize, false);

    // 处理非原始对象,如String,数组,Object
    desc.getObjFieldValues(obj, objVals);    
    // 记录bin的字段值bin: 0003 62 69 6E
    writeObject0(objVals[i], fields[numPrimFields + i].isUnshared());    
}

需要注意的,writeSerialData()中,会通过slotDesc.hasWriteObjectMethod()检查被序列化的对象是否实现了签名为private void writeObject(ObjectOutputStream out)的方法, 如果有该方法,将直接调用该方法。该方法可以提供给用户自定义字段内容的存储格式,如密码的加密存储。

同样,ObjectInputStream.readObject时,也会检查反序列化对象是否实现了签名为private void readObject(ObjectInputStream in)的方法,如果有该方法, 将直接该方法反序列化。该方法可实现反序列化时单例的需求。