主要用于存储对象状态为另一种通用格式,比如存储为二进制、xml、json等等,把对象转换成这种格式就叫序列化,而反序列化通常是从这种格式转换回来。
序列化过程: 是指把一个Java对象变成二进制内容,实质上就是一个byte[]数组。 因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程(IO),这样,就相当于把Java对象存储到文件或者通过网络传输出去了。
反序列化过程: 把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象。
使用序列化主要是因为跨平台和对象存储的需求,因为网络上只允许字符串或者二进制格式,而文件需要使用二进制流格式,如果想把一个内存中的对象存储下来就必须使用序列化转换为xml(字符串)、json(字符串)或二进制(流)
以下是一些使用序列化的示例:
-以面向对象的方式将数据存储到磁盘上的文件,例如,Redis存储Student对象的列表。 -将程序的状态保存在磁盘上,例如,保存游戏状态。 -通过网络以表单对象形式发送数据,例如,在聊天应用程序中以对象形式发送消息。
两个程序在内存空间是完全独立的,默认情况下是互相不能访问的,那么两个程序如何进行复制数据(列表,字典等)的交换?
因为硬盘上只能存储字符串或二进制形式的数据,不能直接将列表或字典写入硬盘。故一个程序通过序列化将数据变成字符串或二进制写入到硬盘,其他程序通过反序列化将二进制或字符串数据成原来的数据读到内存进行操作。
例如玩游戏时,正常下状态是在内存中动态生成。但是当电脑宕机,开机时会恢复到原来状态。因为游戏会实时将状态保存到硬盘,每隔10秒保存最新状态。
首先必须将其变成字符串,传到客户端,客户端在反序列化读数据。即在两个独立的服务器之间完成内存的共享例如,nosql。例如快递一样,A包装快递(即序列化),通过车运输(即socket),B收到拆开快递(即反序列化)。
json格式,一种标准化的数据格式,将数据json化类似于序列化
Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。必须注意地是,对象序列化保存的是对象的"状态",即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。
除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象序列化。
在很多应用场景中都需要用到序列化和反序列化,以保存当前对象的状态信息或者用于传输,例如,在常见的web服务中的持久化session就是一个很好的例子,一般session都是入驻内存的,当服务器异常宕机,内存里的session因为掉电而搽除,当我们设置了session持久化特性时,就会把session保存在硬盘上,这就是序列化,等服务器重启后有可以读取硬盘上这个session文件,还原session对象,这就是反序列化。
另一个需要序列化的场景就是进程间的远程通信,远程通信是以二进制字节流传输的,当需要传输对象的时候,首先在发送端需要将对象序列化为二进制形式方便网络传输,然后在接收端将二进制形式反序列化为java对象。比如早期的RMI,甚至现在流行的RPC通信都用到了序列化和反序列化。
/**
不实现Serializable时
java.io.NotSerializableException: TEst.Box
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at TEst.SerializableTest.main(SerializableTest.java:19)
**/
public class Box implements Serializable {
private int width;
private int height;
public Box(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public String toString() {
return "Child{" +
"width=" + width +
", height=" + height +
'}';
}
}
public class SerializableTest {
public static void main(String[] args) {
try {
File file = new File("a.out");
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
Box box = new Box(10,20);
objectOutputStream.writeObject(box);
objectOutputStream.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fis);
Box newBox = (Box)in.readObject();
in.close();
System.out.println(newBox.toString());
System.out.println(file.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
}
//Child{width=10, height=20}
/Users/lihe/workspace/SpringCrane/a.out
其中new File();表示在该项目的目录下,不是mac目录
静态成员属于类级别的,所以不能序列化,这里的不能序列化的意思,是序列化信息中不包含这个静态成员域
public class Box implements Serializable {
private int width;
private int height;
private static int count = 0;
public Box(int width, int height) {
this.width = width;
this.height = height;
count++;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public static int getCount() {
return count;
}
public static void setCount(int count) {
Box.count = count;
}
@Override
public String toString() {
return "Box{" +
"width=" + width +
", height=" + height +
", count=" + count +
'}';
}
}
public class SerializableTest {
public static void main(String[] args) {
try {
File file = new File("a.out");
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
Box box = new Box(10,20);
objectOutputStream.writeObject(box);
objectOutputStream.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fis);
Box newBox = (Box)in.readObject();
in.close();
System.out.println(newBox.toString());
System.out.println(file.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
}
//Box{width=10, height=20, count=1}
public class TestBox {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("a.out");
ObjectInputStream in = new ObjectInputStream(fis);
Box newBox = (Box)in.readObject();
in.close();
System.out.println(newBox.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//Box{width=10, height=20, count=0}
类SerializableTest 的运行结果显示count=1,似乎被序列化了,但是类TestBox的运行结果显示count=0并未被序列化。
”序列化保存的是对象的状态,静态变量数以类的状态,因此序列化并不保存静态变量。
这里的不能序列化的意思,是序列化信息中不包含这个静态成员域
SerializableTest 测试成功,是因为都在同一个机器(而且是同一个进程),因为这个jvm已经把count加载进来了,所以你获取的是加载好的count,如果你是传到另一台机器或者你关掉程序重写写个程序读入test.obj,此时因为别的机器或新的进程是重新加载count的,所以count信息就是初始时的信息。
serialVersionUID适用于JAVA的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用;
序列化到同一个文件时,如第二次修改了相同对象属性值再次保存时候,虚拟机根据引用关系知道已经有一个相同对象已经写入文件,因此只保存第二次写的引用,所以读取时,都是第一次保存的对象。