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

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

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


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

static final long serialVersionUID = -3387516993124229948L;



序列化的概念

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

反过来说就是反序列化。


序列化的应用


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


2、网络上远程传输对象。



提到serialVersionUID,可能有些人很久自己写过了。其实你还是经常用的。
即使你用json进行序列化远程传输,依旧要用到serialVersionUID。
即使是String也是一样。




java 反序列化 yaml java 反序列化代码执行测试_java序列化





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

不用太多语言描述,语言描述反而看不懂,我自己是这么认为。

上代码,不喜欢看代码的,请看注释。



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


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 反序列化 yaml java 反序列化代码执行测试_java序列化_02




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




原因大家也都很清楚,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 反序列化 yaml java 反序列化代码执行测试_java 反序列化 yaml_03





先忽略执行一遍看看结果。


java 反序列化 yaml java 反序列化代码执行测试_java序列化_04





成功了,赶紧庆祝一下。


为什么有警告?说一说serialVersionUID


警告在提示,没有serialVersionUID


自动生成:如果没有会根据类名、属性、方法等自动生成一个serialVersionUID,所以才是警告而非错误。


辅助添加:有两种方式



java 反序列化 yaml java 反序列化代码执行测试_java 反序列化 yaml_05




第一种方式生成的是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。