Java序列化细节及实例
静态变量的序列化
代码:
public class Test01 implements Serializable {
static int a = 10;
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("re.obj"));
objectOutputStream.writeObject(new Test01());
objectOutputStream.close();
Test01.a = 50;
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("re.obj"));
Test01 test01 = (Test01) objectInputStream.readObject();
System.out.println(test01.a);
objectInputStream.close();
}
}
结果输出50原因在于序列化时,并不保存静态变量,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
简单字段加密
代码:
public class pass implements Serializable{
private static final long serialVersionUID = 1L;
private String password = "pass";
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private void writeObject(ObjectOutputStream out) {
try {
PutField putFields = out.putFields();
System.out.println("原密码:" + password);
password = "encryption";//模拟加密
putFields.put("password", password);
System.out.println("加密后的密码" + password);
out.writeFields();
} catch (IOException e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream in) {
try {
GetField readFields = in.readFields();
Object object = readFields.get("password", "");
System.out.println("要解密的字符串:" + object.toString());
password = "pass";//模拟解密,需要获得本地的密钥
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("result.obj"));
out.writeObject(new pass());
out.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
"result.obj"));
pass t = (pass) oin.readObject();
System.out.println("解密后的字符串:" + t.getPassword());
oin.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出:
原密码:pass
加密后的密码encryption
要解密的字符串:encryption
解密后的字符串:pass
序列化存储规则
代码:
public class Test02 implements Serializable{
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.obj"));
Test02 test02 = new Test02();
objectOutputStream.writeObject(test02);
objectOutputStream.flush();
System.out.println(new File("a.obj").length());
objectOutputStream.writeObject(test02);
objectOutputStream.close();
System.out.println(new File("a.obj").length());
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.obj"));
Test02 t1 = (Test02) objectInputStream.readObject();
Test02 t2 = (Test02) objectInputStream.readObject();
System.out.println(t1==t2);
}
}
输出:
40
45
true
对同一对象两次写入文件,打印出写入一次对象后的存储大小和写入两次后的存储大小,然后从文件中反序列化出两个对象,比较这两个对象是否为同一对象。 可以看到:第二次写入对象时文件只增加了 5 字节,并且两个对象是相等的, Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,上面增加的 5 字节的存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系,使得清单 3 中的 t1 和 t2 指向唯一的对象,二者相等,输出 true。该存储规则极大的节省了存储空间。
再有:
public class Test03 implements Serializable{
int a = 10;
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.obj"));
Test03 test03 = new Test03();
objectOutputStream.writeObject(test03);
objectOutputStream.flush();
test03.a = 50;
objectOutputStream .writeObject(test03);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.obj"));
Test03 t1 = (Test03) objectInputStream.readObject();
Test03 t2 = (Test03) objectInputStream.readObject();
System.out.println(t1.a);
System.out.println(t2.a);
}
}
将 test03对象两次保存到 a.obj 文件中,写入一次以后修改对象属性值再次保存第二次,然后从 a.obj 中再依次读出两个对象,输出这两个对象的 a性值。结果两个输出的都是10, 原因就是第一次写入对象以后,第二次再试图写的时候,虚拟机根据引用关系知道已经有一个相同对象已经写入文件,因此只保存第二次写的引用,所以读取时,都是第一次保存的对象。所以在使用一个文件多次 writeObject 需要特别注意这个问题。