Java序列化Serialize
序列化与反序列化
序列化:把对象写入到流中
反序列化:把对象从流中读取出来
什么情况下序列化
- 对象需要通过网络进行传输
- 需要持久化对象到磁盘
- 需要持久化对象到数据库(把对象通过字节流的方式存储)
序列化的实现方式
实现Serializable接口
- Serializable是一个标记接口,接口中没有任何方法
- 需要序列化的对象除基本数据类型属性外其他属性也必须实现Serializable接口
public class Student implements Serializable{
private static final long serialVersionUID = 2992794326818594180L;
private String name;
private int age;
//省略constructor、setter、getter、toString
}
@Test
public void test1() throws Exception {
Student s1=new Student("tom",20);
System.out.println(s1);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
oos.writeObject(s1);
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s2=(Student)ois.readObject();
System.out.println(s2);
}
输出:
Student [name=tom, age=20]
Student [name=tom, age=20]
如果序列化对象的非基本数据类型属性没有实现Serialize接口,会抛出NotSerializableException异常
public class Student implements Serializable{
private static final long serialVersionUID = 2992794326818594180L;
private String name;
private int age;
private School school;
//省略constructor、setter、getter、toString
}
class School{
private String name;
//省略constructor、setter、getter、toString
}
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
Student s1=new Student("tom",20,new School("xw"));
System.out.println(s1);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
oos.writeObject(s1);
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s2=(Student)ois.readObject();
System.out.println(s2);
}
输出:
java.io.NotSerializableException: com.moyuduo.analyze.School
实现Externaliable接口并实现方法
- 序列化对象的非基本数据类型外其他类型必须实现Serializable接口或Externaliable接口
- 实现Externaliable的类必须提供无参构造器,因为在反序列化的时候需要使用无参构造器创建对象
这种方式比实现Serializable接口略复杂,但是可以实现更加复杂的控制,因为实现Externaliable接口重写方法需要我们自己指定序列化规则(可以对序列化进行加密产生字节)和反序列化规则(序列化规则的逆过程)
实现Externaliable接口方式比实现Serializable接口方式的效率高
public class Student implements Externalizable{
private String name;
private int age;
private School school;
public Student() {//必须加无参构造器
System.out.println("Student无参构造器");
}
//省略有参constructor、setter、getter、toString
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(new StringBuffer(this.name).reverse().toString());
out.writeInt(this.age);
out.writeObject(this.school);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name=new StringBuffer((String)in.readObject()).reverse().toString();
this.age=in.readInt();
this.school=(School)in.readObject();
}
}
class School implements Externalizable{
private String name;
public School() {
System.out.println("School无参构造器");
}
//省略有参constructor、setter、getter、toString
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(new StringBuffer(this.name).reverse().toString());
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name=new StringBuffer((String)in.readObject()).reverse().toString();
}
}
public static void main(String[] args) throws Exception {
Student s1=new Student("tom",20,new School("xh"));
System.out.println(s1);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
oos.writeObject(s1);
oos.close();
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s2=(Student)ois.readObject();
System.out.println(s2);
ois.close();
}
输出:
Student [name=tom, age=20, school=School [name=xh]]
Student无参构造器
School无参构造器
Student [name=tom, age=20, school=School [name=xh]]
部分属性序列化
使用transient关键字
被transient关键字修饰的属性,在对象序列化时不会序列化,而且反序列化得到的该属性值是默认值
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private transient int age;
//省略constructor、setter、getter、toString
}
public static void main(String[] args) throws Exception{
Student s=new Student("tom",20);
System.out.println(s);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
oos.writeObject(s);
oos.close();
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s2=(Student)ois.readObject();
System.out.println(s2);
ois.close();
}
输出:
Student [name=tom, age=20]
Student [name=tom, age=0]
自定义属性序列化
- 可以实现Externalizable接口并在序列化方法writeExternal中序列化需要的属性
- 可以实现Serializable接口,并自己定义
private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;
writeObject方法序列化需要的属性
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
//省略constructor、setter、getter、toString
private void writeObject(ObjectOutputStream out) throws IOException{
out.writeObject(this.name);
}
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{
this.name=(String)in.readObject();
}
}
public static void main(String[] args) throws Exception{
Student s=new Student("tom",20);
System.out.println(s);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
oos.writeObject(s);
oos.close();
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s2=(Student)ois.readObject();
System.out.println(s2);
ois.close();
}
输出:
Student [name=tom, age=20]
Student [name=tom, age=0]
序列化对象的static属性
在对象进行序列化时,被static修饰的属性并不会进行序列化
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
public static int age;
//省略constructor、setter、getter、toString
}
public static void main(String[] args) throws Exception{
Student s=new Student("tom");
Student.age=20;
System.out.println(s);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
oos.writeObject(s);
oos.close();
Student.age=30;
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s2=(Student)ois.readObject();
System.out.println(s2);
ois.close();
}
输出:
Student [name=tom,age=20]
Student [name=tom,age=30]
可以看到Student的static属性age并没有被序列化输出
实现Serializable的类需要提供一个serialVersionUID
serialVersionUID是用于确定版本信息的,如果不指定JVM会根据类信息自动生成一个,JVM会根据两个serialVersionUID判断是否是同一个类,如果serialVersionUID不一致,会抛出InvalidClassException异常
一般建议显式指定serialVersionUID,如果类中添加了新的属性,而想进行向下兼容的话,可以不改变serialVersionUID,那么反序列化后新添加的属性就是默认值
如果删除了类的属性,就需要修改serialVersionUID
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private int age;
//省略constructor、setter、getter、toString
}
//把对象写出去
@Test
public void test4() throws IOException {
Student s=new Student("tom",20);
System.out.println(s);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
oos.writeObject(s);
oos.close();
}
//删除Student的age属性,并修改serialVersionUID
public class Student implements Serializable{
private static final long serialVersionUID = 2L;
private String name;
//省略constructor、setter、getter、toString
}
//读取对象
public static void main(String[] args) throws Exception{
Student s=new Student("tom");
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/student.txt"));
Student s2=(Student)ois.readObject();
System.out.println(s2);
ois.close();
}
输出:
Exception in thread "main" java.io.InvalidClassException: com.moyuduo.analyze.Student; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2