Java序列化

Java序列化就是将Java对象转换成字节流,可用于持久化存储,或者网络传输等。反序列化就是将字节流再转成对象。

1、序列化使用

在Java中,类实现了java.io.Serializable接口就可以被序列化。Java的枚举默认继承类java.lang.Enum也实现了Serializable接口,所以枚举类型对象都是默认可序列化的。另外数组也是可以序列化的。

那通过实现Serializable的类如何进行序列化和反序列化呢。
下面是一个简单的序列化代码,将person对象序列化后写入到file文件中。

ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
Persion persion = new Persion();
oout.writeObject(person);
oout.close();

在反序列化过程中,重新读取对象时,没有调用构造器,直接能将对象还原。(其中读取程序要确保ClassPath中包含读取的类Class,否则抛出ClassNotFoundException。)

ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
Object newPerson = oin.readObject();
Oin.close();

2、序列化机制

默认序列化

只实现了Serializable接口的话,则使用默认机制,不仅序列化该对象本身,还会序列化引用的其他对象。同样,这些引用的对象引用的其他对象也会被序列化。

如一个成员变量是容器,这个成员变量也会被序列化(很多集合类都实现了Serializable接口),容器中包含的元素也会被序列化。这样过程就很复杂,开销也很大。

影响序列化

有的时候不想使用默认的序列化机制,如有些字段想忽略,有些数据想加密等,有下面几种影响序列化的方式:

  • transient关键字
    被声明为transient的字段,在默认序列化中会被忽略。
transient private String name;
  • writeObject()与readObject()方法
    被序列化的对象中添加writeObject(ObjectOutputStream out)与readObject(ObjectInputStream in)方法(都是private void),可以覆盖序列化和反序列化过程。
transient private String name;

private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();//默认序列化机制
    out.writeString(name);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();//默认序列化机制
    name = in.readString();
}
  • Externalizable接口
    继承Externalizable接口则基于Serializable机制失效,该接口需要实现writeExternal(ObjectOutput out)与readExternal(ObjectInput in)方法,在这两个方法中实现序列化的过程。
public class Person implements Externalizable {
    private ind id;
    transient private String name;
    public Person() {
        System.out.println("反序列化readExternal时调用该构造函数");
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(id);
        out.writeObject(name);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        id = in.readInt();
        name = (String) in.readObject();
    }
}

使用Externalizable序列化过程,读取对象时会调用类的无参构造器来创建一个对象,然后再调用readExternal(),可以在方法中用被保存的属性值填充到新对象中。

  • readResolve()方法
    如果类实现了Object readResolve()方法,则不管Serializable还是Externalizable接口都失效,读取对象时获得的是该方法返回的对象。

序列化版本

Java反序列化对象时,需要这个对象类的class文件来感知类的结构。这个类结构是有可能变化的,如增加修改字段等,所以我们一般会对需要序列化反序列化的类中定义serialVersionUID

private static final long serialVersionUID

这个serialVersionUID就是用来版本控制的。类升级会向下兼容的话,这个serialVersionUID就不需要修改,但如果不兼容的话,可以修改serialVersionUID值。如果需要反序列化的对象中serialVersionUID与当前引用的class文件中的serialVersionUID不一致的话,会报版本不一致的错误。

如果不定义这个serialVersionUID,Java会自动生成这个值,而修改类文件重新编译后,这个类再生成的serialVersionUID值可能会发生变化,这时就会导致两个serialVersionUID版本不一致而反序列化失败。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值