序列化和反序列化

什么是序列化和反序列化?

把对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为对象的过程称为对象的反序列化

总之,序列化和反序列化操作的是java对象(Object)

序列化:把java对象转换成二进制码

反序列化:把java对象的二进制码转换回java对象

对象的序列化主要有两种用途:
  1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2) 在网络上传送对象的字节序列。

  在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

  当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

如何实现序列化和反序列化?

1.让想要可序列化的对象实现空接口Serializable,意思是表示该类的对象可序列化.

2.使用java.io包内的序列化API来实现序列化和反序列化.

可序列化API

java.io.ObjectOutputStream :

  public ObjectOutputStream(OutputStream out) : 构造函数,以OutputStream对象作为参数

  public final void writeObject(Object obj) : 把对象转换成二进制码并写入到本地文件

java.io.ObjectInputStream :

  public ObjectInputStream(InputStream in) : 构造函数,以InputStream对象作为参数

  public final Object readObject() : 读取本地文件中保存的二进制码,并转换为Object对象

实例

Person类:

public class Person implements Serializable {
   //序列化的版本号,下边将会介绍
   private static final long serialVersionUID = 4744551959239088922L;
   private int id;
    private String name;
    private String sex;
    private int age;
    private String desc;

    public Person(int id, String name, String sex, int age, String desc) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.desc = desc;
    }

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", desc=" + desc + "]";
	}
}

  

序列化和反序列化测试类:

public class TestObjSerializeAndDeserialize {
	File File = new File("C:/test/person.txt");

	/**
	 * 序列化,把Person对象序列化并存储到本地文件中(C:/test/person.txt)
	 */
	@Test
    public  void serializePerson() {
        Person p = new Person(1,"张三", "男", 18, "废人一个");
        try {
            ObjectOutputStream objOutStream = new ObjectOutputStream(new FileOutputStream(File));
            objOutStream.writeObject(p);
            System.out.println("对象序列化成功");
            objOutStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	/**
	 * 反序列化,读取本地文件的二进制内容,反序列化为Person对象
	 */
	@Test
	public void deserialize(){
		try {
			ObjectInputStream objInStream = new ObjectInputStream(new FileInputStream(File));
			Person person = (Person)objInStream.readObject();
			System.out.println(person);
			objInStream.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}


}

什么是serialVersionUID?

  凡是实现了Serializable接口的类,都会有一个s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D静态常量

  如果没有手动设定该变量,那么IDE会报警告,如下图

  

  点击Add generated serial version ID即可根据类文件自动生成s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D变量.

为什么要显式地设置s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D?

1.避免修改对象的类内容导致的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D值不一致 :

  如果没有显式地声明s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D静态常量,那么Java编译器会自动给该类生成一个唯一的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D,只要类的内容稍有不同,

  该类的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D值就会完全不同.由此我们可以推断出 :

    如果把对象序列化存储到本地后,我们修改了对象的类的内容,那么该类的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D也会不同了,

    这时我们反序列化该类,调用readObject()方法时,就会报异常:java.io.InvalidClassException,内容为前后serialVersionUID值不匹配

  如果我们显式地声明了s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D静态常量;就可以避免该异常,即使修改类的内容,也可以正常进行反序列化.

2.避免相同类文件用不同java编译器导致的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D值不一致 : 

  类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,

  有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,

  强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值

序列化并存储到本地文件后,打开文件的乱码问题 :

正常.序列化后,输出的完全是二进制内容,所以是乱码

原文地址:https://www.cnblogs.com/jinyu59/p/10780410.html