Java中的序列化与反序列化
简述
序列化就是将某个对象转换成特定的数据格式,反序列化即序列化的返操作。一般将序列化后的数据通过网络传输或者保存到数据库。
java的序列化操作
在Java中,若要将一个对象可序列化则要实现 Serializable
接口。且尽可能标记属性serialVersionUID
(后面Test将说明为什么要标记此属性),一般IDEA会自动生成。
- 如我有一列
class Order implements Serializable {
private static final long serialVersionUID = 1L;
private String orderNo;
private int amount;
//构造
//getter setter...
@Override
public String toString() {
//所有属性
}
}
现在将利用java.io
包下的ObjectOutputStream
和FileOutputStream
来演示如何将Order
对象序列化到文件里保存起来
public class SerializableTest {
public final static String PATH = "/tmp/order.txt";
@Test
public void test_serializable() throws IOException {
Order o = new Order("abc", 1299, new Address("安徽"), new Person("Savey"));
try ( FileOutputStream fstream = new FileOutputStream(PATH);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fstream)
) {
//接收一个对象、并将对象转为字节流
objectOutputStream.writeObject(o);
//刷新到输出fstream
objectOutputStream.flush();
}
}
}
此时 Order
对象已经被序列化到 /tmp/order.txt文件里。
java的反序列化操作
如上回说到Order
已经被我们保存到指定文件,此时我们想读取数据则这样处理
@Test
public void test_covert_object() throws IOException, ClassNotFoundException {
try (FileInputStream fileInputStream = new FileInputStream(PATH);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)
) {
//从一个输入流读取数据转为对象,这里加个强转为Order
Order o = (Order)objectInputStream.readObject();
System.out.println(o);
}
}
此时我们利用了几个重要的对象帮助我们序列和反序列操作
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
注意事项
serialVersionUID
假如我在序列化之前的 Order
对象是这样的
class Order implements Serializable {
private static final long serialVersionUID = 1L;
}
在反序列化之后serialVersionUID
变了
class Order implements Serializable {
private static final long serialVersionUID = 2L;
}
那么不好意思,会报错,如下:
java.io.InvalidClassException: Order; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
序列化对象里有其他自定义对象
比如我在Order
里加了一个Person
对象
class Order implements Serializable {
private static final long serialVersionUID = 1L;
//其他属性
private Person person;
//getter setter construct toString
}
class Person implements Serializable {
private static final long serialVersionUID = 2L;
}
如果Person
没有实现Serializable
接口也会出错,如下:
java.io.NotSerializableException: Person
But 你可以修改Person
的申明方式
private transient Person person;
用关键字transient
来修饰您不想序列化的属性!在ArrayList
源码里 是不是也见过这个关键字呢???
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
自定义序列化
不错,您还可以自定义你要序列化的内容,java很优雅的为我们提供了两个方法重写序列化的方法
private void writeObject(ObjectOutputStream oos)
private void readObject(ObjectInputStream ois)
这两个方法见名思意,一个写入、一个读取。如上回说到一个属性被关键写transient
修饰了,但我还想将它序列化下~
class Order implements Serializable {
//其他属性
private transient Person person;
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
//自定义写入Person这个对象、尽管你是 transient修饰了!!
oos.writeObject(person);
}
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
//读取强行转成Person对象
Person person = (Person) ois.readObject();
this.setPerson(person);
}
//getter setter construct
}
上面代码演示了如果自定义序列化和反序列化,如果你有多个属性要自定义操作,请注意的写入的顺序,和读取的顺序要一一对应,不然会出错的哦!
java.lang.ClassCastException: XXX cannot be cast to XXX
代码在GitHub
本文来自博客园,作者:乌托拉赛文,转载请注明原文链接:https://www.cnblogs.com/m78-seven/articles/15218763.html