java序列化
序列化的作用
序列化的必要性
如何序列化
public interface Serializable
import java.io.Serializable;
public class Student implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String Stu_no;
private String Stu_name;
private int age;
public Student(){
}
public Student(String Stu_no,String Stu_name,int age){
this.age = age;
this.Stu_name = Stu_name;
this.Stu_no = Stu_no;
}
public String getStu_no() {
return Stu_no;
}
public void setStu_no(String stu_no) {
Stu_no = stu_no;
}
public String getStu_name() {
return Stu_name;
}
public void setStu_name(String stu_name) {
Stu_name = stu_name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class TestSeria {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Student stu = new Student("12072125","Amiy",19);
FileOutputStream out = new FileOutputStream("D:\\amiy.out");
ObjectOutputStream obout = new ObjectOutputStream(out);
obout.writeObject(stu);
obout.flush();
obout.close();
}
}
以下是将持久的文件中读取重建新对象:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class TestSeria {
/**
* @param args
* @throws IOException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
//Student stu = new Student("12072125","Amiy",19);
FileInputStream out = new FileInputStream("D:\\amiy.out");
ObjectInputStream obout = new ObjectInputStream(out);
Student Jimy = (Student) obout.readObject();
}
}
对象的序列化格式
序列化算法
java序列化安全问题
Serializable
对象上提供一个
writeObject
方法来做到这一点。
模糊化序列化数据
假设Student类中的敏感数据是 age 字段。毕竟,女士忌谈年龄。 我们可以在序列化之前模糊化该数据,将数位循环左移一位,然后在反序列化之后复位。(您可以开发更安全的算法,当前这个算法只是作为一个例子。)
为了 “hook” 序列化过程,我们将在Student上实现一个 writeObject
方法;为了 “hook” 反序列化过程,我们将在同一个类上实现一个readObject
方法。重要的是这两个方法的细节要正确 — 如果访问修改方法、参数或名称的内容,那么代码将不被察觉地失败,Student的 age 将暴露。
private void writeObject(java.io.ObjectOutputStream stream)
throws java.io.IOException
{
// "Encrypt"/obscure the sensitive data
age = age << 2;
stream.defaultWriteObject();
}
private void readObject(java.io.ObjectInputStream stream)
throws java.io.IOException, ClassNotFoundException
{
stream.defaultReadObject();
// "Decrypt"/de-obscure the sensitive data
age = age << 2;
}
序列化的数据可以被签名和密封
上一个技巧假设您想模糊化序列化数据,而不是对其加密或者确保它不被修改。当然,通过使用 writeObject
和 readObject
可以实现密码加密和签名管理,但其实还有更好的方式。
如果需要对整个对象进行加密和签名,最简单的是将它放在一个 javax.crypto.SealedObject
和/或 java.security.SignedObject
包装器中。两者都是可序列化的,所以将对象包装在 SealedObject
中可以围绕原对象创建一种 “包装盒”。必须有对称密钥才能解密,而且密钥必须单独管理。同样,也可以将 SignedObject
用于数据验证,并且对称密钥也必须单独管理。
结合使用这两种对象,便可以轻松地对序列化数据进行密封和签名,而不必强调关于数字签名验证或加密的细节。
transient关键字
探讨serialVersionUID
在上文中,我们描述序列化原理时,曾经提及每个对象都会有一个唯一的序列号,这个序列号,就是serialVersionUID。
当我们的对象实现Serializable接口时,该接口可以为我们生成serialVersionUID。
有两种方式来生成serialVersionUID,一种是固定值:1L,一种是经过JVM计算,不同的JVM采取的计算算法可能不同。
下面就是两个serialVersionUID的示例:
1 private static final long serialVersionUID = 1L; 2 private static final long serialVersionUID = -2380764581294638541L;
第一行是采用固定值生成的;第二行是JVM经过计算得出的。
那么serialVersionUID还有其他用途吗?
我们可以使用它来控制版本兼容。如果采用JVM生成的方式,我们可以看到,当我们业务对象的代码保持不变时,多次生成的serialVersionUID也是不变的,当我们对属性进行修改时,重新生成的serialVersionUID会发生变化,当我们对方法进行修改时,serialVersionUID不变。这也从另一个侧面说明,序列化是作用于对象属性上的。
当我们先定义了业务对象,然后对其示例进行了“序列化”,这时根据业务需求,我们修改了业务对象,那么之前“序列化”后的内容还能经过“反序列化”返回到系统中吗?这取决于业务对象是否定义了serialVersionUID,如果定义了,那么是可以返回的,如果没有定义,会抛出异常。