简述
序列化就是将某个对象转换成特定的数据格式,反序列化即序列化的返操作。个别将序列化后的数据通过网络传输或者保留到数据库。
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