首先需要说明的是,目前的传输和储存多以json为主,连xml应用看上去都不那么广泛了。
于是,我们需要对自己写的类进行序列化的操作机会变少了。
但,若我们对序列化有了了解,总是对于理解一些问题有帮助的。(其中序列化三个字可以换成任何技术)
今天要说的就是下面这个东西
static final long serialVersionUID = -3387516993124229948L;
序列化的概念
序列化:把对象转化为字节序列的过程。
反过来说就是反序列化。
序列化的应用
1、储存对象,可以是永久的储存在硬盘的一个文件上,也可以是储存在redis支持序列化存储的容器中。
2、网络上远程传输对象。
提到serialVersionUID,可能有些人很久自己写过了。其实你还是经常用的。
即使你用json进行序列化远程传输,依旧要用到serialVersionUID。
即使是String也是一样。
即使你用json进行序列化远程传输,依旧要用到serialVersionUID。
即使是String也是一样。
下面用代码说明一下序列化的过程
不用太多语言描述,语言描述反而看不懂,我自己是这么认为。上代码,不喜欢看代码的,请看注释。
序列化和反序列化的测试类
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方法,控制台报错
这个错误,不做多解释了,英文很好理解。
原因大家也都很清楚,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;
}
}
发现有警告
先忽略执行一遍看看结果。
成功了,赶紧庆祝一下。
为什么有警告?说一说serialVersionUID
警告在提示,没有serialVersionUID
自动生成:如果没有会根据类名、属性、方法等自动生成一个serialVersionUID,所以才是警告而非错误。
辅助添加:有两种方式
第一种方式生成的是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
local class incompatible:
stream classdesc serialVersionUID = -6367006705587584157,
local class serialVersionUID = 5139856059457605582
原因就是两次生成的serialVersionUID 不同。
结论:
环境的变化会导致生成serialVersionUID的变化,例如不同的编译器生成的serialVersionUID有可能不同
被序列化类本身的变化,会导致serialVersionUID发生变化
这可能不是我们想看到的。
serialVersionUID应该是唯一的,确定的,独立的。
应该显示的定义serialVersionUID,并赋予明确的、唯一确定的值。
作用:
在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID。
在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。