序列化和反序列化

  • 1 什么是序列化和反序列化
  • 2 如何使用
  • 3 为什么使用serialVersionUID


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

1、定义:
  把对象转换为字节序列的过程称为对象的序列化。
  把字节序列恢复为对象的过程称为对象的反序列化。
2、用途:
  (1)需要把一些对象持久保存起来,通过序列化将内存种的这些对象转换为一系列的字节,就是变成文件。
  (2)需要在网络上传送字节序列。

2 如何使用

1、先看一下接口Serializable的源代码。

package java.io;
// 一大堆注释
public interface Serializable {
}

空的?一个空的方法怎么实现对象的序列化和反序列化的?其实注释里已经提到了,它只是一个标志而已,告诉JVM实现这个接口的类的对象可被序列化。
2、上代码

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Student implements Serializable{
	private static final long serialVersionUID = 1l;
	public static String school = "Java School";
	public int id;
	public String name;
	transient String address;
	public Student(int id, String name, String address) {
		this.id = id;
		this.name = name;
		this.address = address;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	@Override
	public String toString() {
		return "Student{id = "+id+", name = "+name+", address = "+address+"}";
	}
}
public class MySeriaalizable{
	public static void main(String[] args) {
		File file = new File("D:/test.txt");
		
		Student s1 = new Student(1001, "Student1", "幸福路1号");
		// 序列化
		try {
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
			out.writeObject(s1);
			out.close();
		}catch (Exception e) {
			e.printStackTrace();
		}
		
		// 反序列化
		try {
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
			Student s2 = (Student) in.readObject();
			System.out.println(s2.toString());
			System.out.println(s1.equals(s2));
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
// 代码输出
Student{id = 1001, name = Student1, address = null}
false

使用起来非常方便,只需要ObjectOutputStream的 writeObject(Object object) 方法实现序列化,ObjectInputStream的readObject() 方法实现反序列化。

代码输出address=null?看一下序列化的结果是什么?

Java socket默认序列化 java接口序列化_序列化


可以看到test.txt文件中还有一些乱码,这不重要。重要的是可以很清楚的看到里面有id, name, 但是没有school, address, 这是因为被transient修饰的字段不会被序列化;另外static修饰的属性属于类,不仅仅属于某个对象,序列化操作的是对象,所以静态变量也不会被序列化。

3 为什么使用serialVersionUID

serialVersionUID非必需但很有必要,如果自己不声明,JVM会动态的生成。原则上,序列化后的数据中的serialVersionUID与当前类当中的serialVersionUID一致时,对象才能够反序列化成功。在序列化过程种,系统将serialVersionUID写入到序列化的文件中,反序列化时,首先检测文件中的serialVersionUID和当前类的serialVersionUID是否一致,如果一致则反序列化成功,否则报错:java.io.InvalidClassException
靠JVM动态声明serialVersionUID,类发生改变serialVersionUID会发生变化,且不同的编译器生成的serialVersionUID也会不同。看一下示例,在部分2中代码基础上,去掉serialVersionUID进行序列化后,对Student类增加一个属性(修改或者删除也一样),然后对序列化结果直接进行反序列化。

class Student implements Serializable{
	//private static final long serialVersionUID = 1l;
	public static String school = "Java School";
	public int id;
	public String name;
	transient String address;
	public String newStr;
	......  // 和部分2代码相同
}
public class MySeriaalizable{
	public static void main(String[] args) {
		File file = new File("D:/test.txt");  // 读取动态生成serialVersionUID序列化的结果
		Student s1 = new Student(1001, "Student1", "幸福路1号");
		
		// 反序列化
		try {
			ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
			Student s2 = (Student) in.readObject();
			System.out.println(s2.toString());
			System.out.println(s1.equals(s2));
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
java.io.InvalidClassException: mytest.Student; local class incompatible: stream classdesc serialVersionUID = -5603698407479578026, local class serialVersionUID = 970959418018661672
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
	at mytest.MySeriaalizable.main(MySeriaalizable.java:61)

错误提示:stream classdesc serialVersionUID=-5603698407479578026, local class serialVersionUID=970959418018661672,两次动态生成的serialVersionUID不同。
如果自己声明serialVersionUID,则不会出现这个错误,在反序列化生成的对象只有未发生改变的属性的赋值,比如将“name”改名为“name1”后的反序列化结果为:

Student{id = 1001, name = null, address = null}
false