对java序列化和反序列化的理解和测试

首先需要说明的是,目前的传输和储存多以json为主,连xml应用看上去都不那么广泛了。

于是,我们需要对自己写的类进行序列化的操作机会变少了。

但,若我们对序列化有了了解,总是对于理解一些问题有帮助的。(其中序列化三个字可以换成任何技术)


今天要说的就是下面这个东西

static final long serialVersionUID = -3387516993124229948L;

序列化的概念

序列化:把对象转化为字节序列的过程。

反过来说就是反序列化。


序列化的应用

1、储存对象,可以是永久的储存在硬盘的一个文件上,也可以是储存在redis支持序列化存储的容器中。
2、网络上远程传输对象。

提到serialVersionUID,可能有些人很久自己写过了。其实你还是经常用的。
即使你用json进行序列化远程传输,依旧要用到serialVersionUID。
即使是String也是一样。
对java序列化和反序列化的理解和测试_第1张图片


下面用代码说明一下序列化的过程

不用太多语言描述,语言描述反而看不懂,我自己是这么认为。
上代码,不喜欢看代码的,请看注释。

序列化和反序列化的测试类
package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SVTest {
	public static void main(String[] args) throws Exception {
		// 序列化User对象
		SerializeUser();
		// 反序列User对象
		User user = DeserializeUser();
		System.out.println(user);
	}

	/**
	 * 序列化User对象
	 */
	private static void SerializeUser() throws FileNotFoundException,
			IOException {
		// 创建一个小明
		User user = new User(1,"小明");
		// ObjectOutputStream 对象输出流
		ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
				new File("D:/User.txt")));
		// 序列化输出User对象
		oo.writeObject(user);
		System.out.println("序列化成功!");
		oo.close();
	}

	/**
	 * 反序列User对象
	 */
	private static User DeserializeUser() throws Exception, IOException {
		// ObjectInputStream 对象读取流
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
				new File("D:/User.txt")));
		// 反序列化User对象
		User user = (User) ois.readObject();
		System.out.println("反序列化成功!");
		ois.close();
		return user;
	}
}



被序列化的User类
package test;

public class User {
	
	// ID
	private int id;
	// 姓名
	private String name;
	
	User(int id, String name) {
		this.id = id;
		this.name = name;
	}

	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;
	}
}

执行main方法,控制台报错
对java序列化和反序列化的理解和测试_第2张图片

这个错误,不做多解释了,英文很好理解。

原因大家也都很清楚,User类没有实现序列化接口。

接下来为User类添加序列化接口的实现
package test;

import java.io.Serializable;

public class User implements Serializable {
	
	// ID
	private int id;
	// 姓名
	private String name;
	
	User(int id, String name) {
		this.id = id;
		this.name = name;
	}

	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;
	}
}

发现有警告
对java序列化和反序列化的理解和测试_第3张图片

先忽略执行一遍看看结果。
对java序列化和反序列化的理解和测试_第4张图片


成功了,赶紧庆祝一下。


为什么有警告?说一说serialVersionUID

警告在提示,没有serialVersionUID
自动生成:如果没有会根据类名、属性、方法等自动生成一个serialVersionUID,所以才是警告而非错误。
辅助添加:有两种方式
对java序列化和反序列化的理解和测试_第5张图片

第一种方式生成的是1L
private static final long serialVersionUID = 1L;

第二种是根据类名、属性、方法等生成的
private static final long serialVersionUID = -6367006705587584157L;

自动生成和辅助添加的生成方式是一样一样的?
是的,一样,或者不同的生成方式原理也相同。

没有也行,有也行,那为啥还会有警告呢,干脆不写算了?
不对,因为辅助添加的动作并不是每次都做,也就是 第一次辅助添加了,以后基本不会变了,即使本类有所改变。
而自动生成,则每次都会改变。

每次都改变又会如何呢?
还是用代码说明, 代码对于程序员来说是最通俗易懂的

将我们添加的 serialVersionUID删除,看看会出现什么结果。
先执行一遍main方法,生成一个User.txt。
成功了!并没有什么影响啊。
继续

为User类性增加一个属性:年龄
package test;

import java.io.Serializable;

public class User implements Serializable {
	
	// ID
	private int id;
	// 姓名
	private String name;
	// 新增加的年龄属性
	private String age;
	
	User(int id, String name) {
		this.id = id;
		this.name = name;
	}

	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;
	}
}

此时, 注释掉main方法中的序列化方法,只执行反序列化方法。会如何呢?

报错了,拒绝载入。

Exception in thread "main" java.io.InvalidClassException: test.User; 
local class incompatible: 
stream classdesc serialVersionUID = -6367006705587584157,
local class serialVersionUID = 5139856059457605582

原因就是两次生成的serialVersionUID 不同。

结论:
环境的变化会导致生成serialVersionUID的变化,例如不同的编译器生成的serialVersionUID有可能不同
被序列化类本身的变化,会导致serialVersionUID发生变化
这可能不是我们想看到的。

serialVersionUID应该是唯一的,确定的,独立的。
应该显示的定义serialVersionUID,并赋予明确的、唯一确定的值。

作用:
在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID。
在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。


你可能感兴趣的:(java)