Java基础:序列化与反序列化

Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。

序列化的使用场景

  • 永久性保存对象,保存对象的字节序列到本地文件或者数据库中
  • 通过序列化以字节流的形式使对象在网络中进行传递和接收
  • 通过序列化在进程间传递对象

序列化的好处

  • 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上
  • 利用序列化实现远程通信,即在网络上传送对象的字节序列

使用Serializable接口实现序列化

package xuliehua;

import java.io.Serializable;

public class Person implements Serializable {

    private static final long serialVersionUID = 6839537839090265344L;
    private int age;
    private String name;
    private String sex;

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    public void setSex(String sex) {
        this.sex = sex;
    }
}
package xuliehua;

import java.io.*;
import java.text.MessageFormat;

public class TestObjSerializeAndDeserialize {
    public static void main(String[] args) throws Exception {
        SerializePerson();//序列化Person对象
        Person p = DeserializePerson();//反序列Perons对象
        System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
                p.getName(), p.getAge(), p.getSex()));
    }
    
    private static void SerializePerson() throws FileNotFoundException, IOException {
        Person person = new Person();
        person.setName("fate");
        person.setAge(25);
        person.setSex("男");
        // ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
                new File("E:/Person.txt")));
        oo.writeObject(person);
        System.out.println("Person对象序列化成功!");
        oo.close();
    }
    
    private static Person DeserializePerson() throws Exception, IOException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                new File("E:/Person.txt")));
        Person person = (Person) ois.readObject();
        System.out.println("Person对象反序列化成功!");
        return person;
    }

}

结果

Person对象序列化成功!
Person对象反序列化成功!
name=fate,age=25,sex=男

对象序列化包括如下步骤:

  • 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  • 通过对象输出流的writeObject()方法写对象。

对象反序列化的步骤如下:

  • 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  • 通过对象输入流的readObject()方法读取对象。

使用Externalizable接口实现序列化

package xuliehua;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class PersonNew implements Externalizable {
    private static final long serialVersionUID = -7103994996403529702L;
    private int age;
    private String name;
    private String sex;

    public PersonNew(){

    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public String getSex() {
        return sex;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(age);
        out.writeObject(name);
        out.writeObject(sex);
    }

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

Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。
当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。
在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。
所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。

Externalizable和Serializable接口的区别

  • Externalizable继承自Serializable接口
  • 需要我们重写writeExternal()与readExternal()方法
  • 实现Externalizable接口的类必须要提供一个public的无参的构造器

serialVersionUID的作用

扫描二维码关注公众号,回复: 12592931 查看本文章

其目的是序列化对象版本控制,有关各版本反序列化时是否兼容。Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。
如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常。
如果修改较小,比如仅仅是增加了一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;
如果我们删除了一个属性,或者更改了类的继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。

serialVersionUID有两种显示的生成方式
一是默认的1L,比如:private static final long serialVersionUID = 1L;
二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;

在实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值

静态变量不会被序列化,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

Transient 关键字作用

Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null

当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口

为一个没有实现Serializable接口的父类,编写一个能够序列化的子类 

1, 父类要有一个无参的constructor;

2, 子类要先序列化自身,然后子类要负责序列化父类的域

猜你喜欢

转载自blog.csdn.net/PrisonJoker/article/details/105320331