在远程方法调用 RMI 学习的过程中,涉及到一个概念,序列化,本文进行详述。
-
Java 对象的序列化和反序列化 的两种应用场景
有时候需要将 Java 对象保存永久保存,比如保存到文件中,过程:
Java 对象 -> IO 对象流 -> 写入文件 -> 字符串
。当我们需要将文档中的字符串恢复为 Java 对象的时候,需要相反的过程:字符串 -> 读文件 -> IO 流 -> (强制)转化为 Java 对象
。
还有一种场景,在网络通信中,对象要先变为IO流经过网络传输之后,再被还原为Java对象。过程:对象 -> 写IO -> 读IO -> 对象
。 -
序列化是一个处理对象信息的过程
序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。
反序列化是将传输信息恢复为对象的过程。 -
Java 对象被序列化的两种方式
方式一:Java 对象所属的类需要直接或者间接实现
Serializable
接口。
方式二:Java 对象所属的类需要直接或者间接实现Externalizable
接口。这里需要注意,如果通过继承实现Externalizable
接口,必须覆盖重写 writeExternal 和 readExternal 方法,否则子类新增属性无法参与序列化。 -
Serializable 和 Externalizable 的区别
Serializable 可以是间接或者直接实现,所在类的属性都会被序列化。
Serializable 的思路是,如果没有特殊,本类以及子类的属性将参与序列化。
Externalizable 也可以是间接实现或者直接实现,但是其参与序列化和反序列化的属相都需要通过重写方法去指定。 -
transient
实现 Serializable 的类 ,其属性默认都会参与序列化和反序列化,如果不想某个属性参与序列化,增加 transient 修饰即可。但是 实现 Externalizable 的类不会受transient 的影响。private transient String other;//transient 表示该字段不参与序列化
Externalizable 需要覆盖重写的 writeExternal 和 readExternal 方法举例
//声明属性
private String userName;
private String password;
private int age;
private int age2;
//get set方法略
//这两个方法无需显式调用,但是哪些属性需要序列化,则需要单独指明
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(password);
out.writeUTF(userName);
out.writeInt(age);
out.writeInt(age2);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
userName=in.readUTF();
password=in.readUTF();
age=in.readInt();
age2=in.readInt();
}
-
序列号的生成
Java 的序列化和反序列化工作是由 JVM 完成的,在序列化和反序列化的过程中,JVM 会根据类的路径、名字、内容生成一个 long 类型的数字串(
当然你也可以自己指定
),这个数字串被称为序列号。private static final long serialVersionUID = -5635658486326609381L;
-
序列号的生成和 JVM 的版本有关系吗?
根据我的实验,对于同一个路径下,同一个类,在内容不变的情况下,JVM 生成的序列号是一致的。测试 jdk 版本有 jdk1.6、jdk1.7、jdk1.8。
-
序列号是允许不显式表述的
序列号是可以不写出来的,但是要求序列化参照的类和反序列化参照的类都不写才可以。
-
序列号的意义
由于序列号的生成是和 类的路径、名字、内容 有关,这意味着如果你的类的内容有修改,JVM 自动生成的序列号就会有变动,
如果你在类中指定了序列号,JVM 就会以你指定的为准
。这样做是有好处的,可以提醒你远程交互中两端的类的版本不一致。但是这也有弊端,对于远程调用来说,如果一方
实体类有变动,那么双方生成的序列号可能就不一样。所以序列号是为了保证序列化和反序列化双方的一致性。 -
一定要显式的指定序列号
由于我们无法确保类的内容不会更改,所以一定要写序列号,这个是可以自定义的,比如写成
1L
。 -
Java 对象序列化会影响性能且需要平台一致性
显然序列化会影响性能,而且需要同为 Java 平台。
-
序列化应用的简单测试——以对象保存到 txt 中为例
- 实体类
package common;
import java.io.Serializable;
public class Model implements Serializable{
private static final long serialVersionUID = -3265803653258922833L;
private String userName;
private String password;
private transient int age;
private int age2;
private transient String other;//transient 表示该字段不参与序列化
//get\set 方法 略
//如果 implements Externalizable ,需在下面的方法中指明需序列化个反序列化字段
//序列化字段
/*@Override public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(password); out.writeUTF(userName); out.writeInt(age); out.writeInt(age2); } //反序列化字段 @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { userName=in.readUTF(); password=in.readUTF(); age=in.readInt(); age2=in.readInt(); }*/
}
- 测试方法
package common;
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 Test {
//文件路径和名字
private static String filePath="D://a.txt";
public static void main(String[] args) {
Model m=new Model();
m.setUserName("jecket");
m.setPassword("123456");
m.setOther("other");
//写到文件
write(m);
//从文件读取
read();
}
//将对象保存到 文件
public static void write(Model m){
try {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(filePath));
oos.writeObject(m);
oos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//从对象中读取 文件
public static void read(){
try {
@SuppressWarnings("resource")
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filePath));
Model m=(Model)ois.readObject();
System.out.println(m.getUserName());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}