序列化和反序列化
- 序列化:将对象变成二进制保存在文件或者数据库中。
- 反序列化:将文件中或数据库中的二进制文件变成一个对象。
对象序列化的两种用途:
- **对象持久化:**把对象的字节序列永久地保存到硬盘上。
- **网络传输对象:**数据是以二进制序列的形式在网络上进行传输的。所以发送方需要把对象转换成字节序列,才能在网络上传送;接收方 则需要把字节序列再恢复成对象。
举例:
Web 服务器中的 Session 会话对象,当有10万用户并发访问,就有可能出现10万个 Session 对象,显然这种情况内存可能是吃不消 的,于是Web 容器就会把一些 Session 先序列化,让他们离开内存空间,序列化到硬盘中,当需要调用时,再把保存在硬盘中的对象还原到内存中。
序列化实例
在Java中,如果一个对象要实现能够反序列或者反序列化,就必须实现Serializable接口,这是个标记接口,里面没有什么方法。
@Data
public class Data implements Serializable {
private static final String DATA_FILE = "DataTest.txt";
// 静态变量,不会参与序列化
public static String one= "1";
// transient变量,没有序列化
public transient String tow;
// 普通的实例常量,可以序列化
private int n;
private String msg;
public Data(String tow, int n, String msg) {
this.tow = tow;
this.n = n;
this.msg = msg;
}
/**
* 序列化
* @throws FileNotFoundException
* @throws IOException
*/
public void serialize() throws FileNotFoundException, IOException {
System.out.println("序列化前静态变量:"+Data.one );
System.out.println("序列化前对象:"+this);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(DATA_FILE));
oos.writeObject(this);
oos.close();
}
/**
* 反序列化
* @throws FileNotFoundException
* @throws IOException
* @throws ClassNotFoundException
*/
public static void deserialize() throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(DATA_FILE));
Data data = (Data) ois.readObject();
System.out.println( "反序列化后静态变量:"+Data.one);
System.out.println("反序列化后对象:"+data);
ois.close();
}
}
测试序列化
public static void main(String[] args)
throws FileNotFoundException, ClassNotFoundException, IOException {
Data data = new Data("2",123, "data-serialization");
Data.one="1024";
System.out.println("--------------序列化开始----------------");
data.serialize();
System.out.println("--------------序列化结束----------------");
}
运行结果:
测试反序列化
public static void main(String[] args)
throws FileNotFoundException, ClassNotFoundException, IOException {
System.out.println("--------------反序列化开始----------------");
Data.deserialize();
System.out.println("--------------序列化结束----------------");
}
运行结果:
注意点:
- transient变量不参与序列化。从上面我们可以看到,transient变量序列化前是2,反序列化后却为null。
- 静态变量是类变量,不是对象变量。所以不参与序列化。从上面我们可以看到, 静态变量序列化前是1024,反序列化后却为初始值1。
- 序列化ID:序列化id相当于是序列化对象的唯一标识。序列化对象时,序列化ID也会被保存在文件中。反序列化时,会检查二进制文件中的序列化ID和这个类中的序列化ID是否一致,一致才能反序列化为对象,不一致则抛出InvalidClassException异常。当类没有定义序列化ID,Java会自动帮我们生成一个。但当我们改变(如新增类的属性)这个类时,则个序列化ID也会改变,导致序列化ID不一致。这时反序列化就会抛出InvalidClassException异常。所以一般我们都会自己定义一个序列化ID,这时我们改变(如新增类的属性)这个类时,序列化ID也会不会改变。就可以反序列化成功。
举例:继续拿上个例子举例,上面我们没有自己定义一个序列化ID
先序列化
public static void main(String[] args)
throws FileNotFoundException, ClassNotFoundException, IOException {
Data data = new Data("2",123, "data-serialization");
Data.one="1024";
System.out.println("--------------序列化开始----------------");
data.serialize();
System.out.println("--------------序列化结束----------------");
}
然后我们在类中新增一个属性
public class Data implements Serializable {
………………其他相同…………………………
//新增属性
private String ms;
…………………其他相同………………………
}
然后反序列化
public static void main(String[] args)
throws FileNotFoundException, ClassNotFoundException, IOException {
System.out.println("--------------反序列化开始----------------");
Data.deserialize();
System.out.println("--------------序列化结束----------------");
}
运行结果:我们可以看到流中的序列化id和本地序列化id不一致,才导致InvalidClassException异常。
现在我们加上序列化id
public class Data implements Serializable {
………………其他相同…………………………
private static final long serialVersionUID = 3604972003323896788L;
…………………其他相同………………………
}
然后序列化
public static void main(String[] args)
throws FileNotFoundException, ClassNotFoundException, IOException {
Data data = new Data("2",123, "data-serialization");
Data.one="1024";
System.out.println("--------------序列化开始----------------");
data.serialize();
System.out.println("--------------序列化结束----------------");
}
再新增属性
public class Data implements Serializable {
………………其他相同…………………………
private static final long serialVersionUID = 3604972003323896788L;
//新增属性
private String ms;
…………………其他相同………………………
}
再反序列化
public static void main(String[] args)
throws FileNotFoundException, ClassNotFoundException, IOException {
System.out.println("--------------反序列化开始----------------");
Data.deserialize();
System.out.println("--------------序列化结束----------------");
}
运行结果:可以看到尽管我新增了一个属性ms,仍能反序列化成功。