Home >Java >javaTutorial >Detailed sample code for Java object serialization and deserialization
This article mainly introduces Java Object serialization and deserialization. The editor thinks it is quite good. Now I will share it with you and give it as a reference. Let’s follow the editor and take a look.
In the previous article, we introduced the use of byte stream character streams. At that time, we used the DataOutputStream stream to output an object to the stream. Each attribute value is output to the stream one by one, and vice versa when read. In our opinion, this behavior is really cumbersome, especially when there are many attribute values in this object. Based on this, the object serialization mechanism in Java can solve this operation very well. This article briefly introduces Java object serialization. The main contents are as follows:
Concise code implementation
Basic algorithm for serialization implementation
Two special situations
Custom serialization mechanism
Serialized version control
1. Simple code implementation
Before introducing how to use object serialization, let’s take a look at how we did it before Stores data of an object type.
//简单定义一个Student类 public class Student { private String name; private int age; public Student(){} public Student(String name,int age){ this.name = name; this.age=age; } public void setName(String name){ this.name = name; } public void setAge(int age){ this.age = age; } public String getName(){ return this.name; } public int getAge(){ return this.age; } //重写toString @Override public String toString(){ return ("my name is:"+this.name+" age is:"+this.age); } }
//main方法实现了将对象写入文件并读取出来 public static void main(String[] args) throws IOException{ DataOutputStream dot = new DataOutputStream(new FileOutputStream("hello.txt")); Student stuW = new Student("walker",21); //将此对象写入到文件中 dot.writeUTF(stuW.getName()); dot.writeInt(stuW.getAge()); dot.close(); //将对象从文件中读出 DataInputStream din = new DataInputStream(new FileInputStream("hello.txt")); Student stuR = new Student(); stuR.setName(din.readUTF()); stuR.setAge(din.readInt()); din.close(); System.out.println(stuR); }
Output result: my name is:walker age is:21
Obviously this kind of code writing is cumbersome. Next, let’s see how to use serialization to complete the save. Object information.
public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt")); Student stuW = new Student("walker",21); oos.writeObject(stuW); oos.close(); //从文件中读取该对象返回 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt")); Student stuR = (Student)ois.readObject(); System.out.println(stuR); }
When writing a file, only one statement is used, writeObject, and only one statement, readObject, is used when reading. And the set and get methods in Student are no longer used. Isn’t it very simple? Implementation details are introduced next.
2. Basic algorithm for serialization
In this mechanism, each object corresponds to a unique serial number, and each When each object is saved, it also corresponds to each different object according to this serial number. Object serialization refers to using the serial number of each object to save and read. First, take writing an object into a stream as an example. For each object, the basic information of the object will be saved into the stream when it is encountered for the first time. If the currently encountered object has been saved, it will not be used again. Save this information and record the serial number of this object instead (because the data does not need to be saved repeatedly). In the case of reading, each object encountered in the stream will be output directly if it is encountered for the first time. If the serial number of an object is read, the associated object will be found and output.
Explain a few points. If an object wants to be serializable, it must implement Interface java.io.Serializable;. This is a mark interface and does not need to implement anything. method. Our ObjectOutputStream stream is a stream that can convert object information into bytes. Constructor is as follows:
public ObjectOutputStream(OutputStream out)
That is, all byte streams can be passed in as parameters and are compatible All byte operations. The writeObject and readObject methods are defined in this stream to implement serialization and deserialization of objects. Of course, we can also customize the serialization mechanism by implementing these two methods in the class, which will be introduced in detail later. Here we only need to understand the entire serialization mechanism. Only one copy of all object data will be saved. As for the same object appearing again, only the corresponding serial number will be saved. Below, we will intuitively experience his basic algorithm through two special situations.
3. Two special examples
Look at the first example first:
public class Student implements Serializable { String name; int age; Teacher t; //另外一个对象类型 public Student(){} public Student(String name,int age,Teacher t){ this.name = name; this.age=age; this.t = t; } public void setName(String name){this.name = name;} public void setAge(int age){this.age = age;} public void setT(Teacher t){this.t = t;} public String getName(){return this.name;} public int getAge(){return this.age;} public Teacher getT(){return this.t;} } public class Teacher implements Serializable { String name; public Teacher(String name){ this.name = name; } } public static void main(String[] args) throws IOException, ClassNotFoundException { Teacher t = new Teacher("li"); Student stu1 = new Student("walker",21,t); Student stu2 = new Student("yam",22,t); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt")); oos.writeObject(stu1); oos.writeObject(stu2); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt")); Student stuR1 = (Student)ois.readObject(); Student stuR2 = (Student)ois.readObject(); if (stuR1.getT() == stuR2.getT()) System.out.println("相同对象"); }
The result is very obvious, output Same object. We defined two student type objects in the main function, but they both reference the same teacher object internally. After the serialization is completed, the two objects are deserialized. By comparing whether their internal teacher objects are the same instance, it can be seen that t is written to the stream when the first student object is serialized, but When encountering the teacher object instance of the second student object, it is found that it has been written before, so it is no longer written to the stream, and only the corresponding sequence number is saved as a reference. Of course, the principle is similar when deserializing. This is the same basic algorithm we introduced above.
Let’s look at the second special example:
public class Student implements Serializable { String name; Teacher t; } public class Teacher implements Serializable { String name; Student stu; } public static void main(String[] args) throws IOException, ClassNotFoundException { Teacher t = new Teacher(); Student s =new Student(); t.name = "walker"; t.stu = s; s.name = "yam"; s.t = t; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt")); oos.writeObject(t); oos.writeObject(s); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt")); Teacher tR = (Teacher)ois.readObject(); Student sR = (Student)ois.readObject(); if(tR == sR.t && sR == tR.stu)System.out.println("ok"); }
输出的结果是ok,这个例子可以叫做:循环引用。从结果我们可以看出来,序列化之前两个对象存在的相互的引用关系,经过序列化之后,两者之间的这种引用关系是依然存在的。其实按照我们之前介绍的判断算法来看,首先我们先序列化了teacher对象,因为他内部引用了student的对象,两者都是第一次遇到,所以将两者序列化到流中,然后我们去序列化student对象,发现这个对象以及内部的teacher对象都已经被序列化了,于是只保存对应的序列号。读取的时候根据序列号恢复对象。
四、自定义序列化机制
综上,我们已经介绍完了基本的序列化与反序列化的知识。但是往往我们会有一些特殊的要求,这种默认的序列化机制虽然已经很完善了,但是有些时候还是不能满足我们的需求。所以我们看看如何自定义序列化机制。自定义序列化机制中,我们会使用到一个关键字,它也是我们之前在看源码的时候经常遇到的,transient。将字段声明transient,等于是告诉默认的序列化机制,这个字段你不要给我写到流中去,我会自己处理的。
public class Student implements Serializable { String name; transient int age; public String toString(){ return this.name + ":" + this.age; } } public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt")); Student stu = new Student(); stu.name = "walker";stu.age = 21; oos.writeObject(stu); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt")); Student stuR = (Student)ois.readObject(); System.out.println(stuR); }
输出结果:walker:0
我们不是给age字段赋初始值了么,怎么会是0呢?正如我们上文所说的一样,被transient修饰的字段不会被写入流中,自然读取出来就没有值,默认是0。下面看看我们怎么自己来序列化这个age。
//改动过的student类,main方法没有改动,大家可以往上看 public class Student implements Serializable { String name; transient int age; private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); oos.writeInt(25); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); age = ois.readInt(); } public String toString(){ return this.name + ":" + this.age; } }
输出结果:walker:25
结果既不是我么初始化的21,也不是0,而是我们在writeObject方法中写的25。现在我们一点一点看看每个步骤的意义。首先,要想要实现自定义序列化,就需要在该对象定义的类中实现两个方法,writeObject和readObject,而且格式必须和上面贴出来的一样,笔者试过改动方法修饰符,结果导致不能成功序列化。这是因为,Java采用反射机制,检查该对象所在的类中有没有实现这两个方法,没有的话就使用默认的ObjectOutputStream中的这个方法序列化所有字段,如果有的话就执行你自己实现的这个方法。
接下来,看看这两个方法实现的细节,先看writeObject方法,参数是ObjectOutputStream 类型的,这个拿到的是我们在main方法中定义的ObjectOutputStream 对象,要不然它怎么知道该把对象写到那个地方去呢?第一行我们调用的是oos.defaultWriteObject();这个方法实现的功能是,将当前对象中所有没有被transient修饰的字段写入流中,第二条语句我们显式的调用了writeInt方法将age的值写入流中。读取的方法类似,此处不再赘述。
五、版本控制
最后我们来看看,序列化过程的的版本控制问题。在我们将一个对象序列化到流中之后,该对象对应的类的结构改变了,如果此时我们再次从流中将之前保存的对象读取出来,会发生什么?这要分情况来说,如果原类中的字段被删除了,那从流中输出的对应的字段将会被忽略。如果原类中增加了某个字段,那新增的字段的值就是默认值。如果字段的类型发生了改变,抛出异常。在Java中每个类都会有一个记录版本号的变量:static final serivalVersionUID = 115616165165L,此处的值只用于演示并不对应任意某个类。这个版本号是根据该类中的字段等一些属性信息计算出来的,唯一性较高。每次读出的时候都会去比较之前和现在的版本号确认是否发生版本不一致情况,如果版本不一致,就会按照上述的情形分别做处理。
对象的序列化就写完了,如果有什么内容不妥的地方,希望大家指出!
The above is the detailed content of Detailed sample code for Java object serialization and deserialization. For more information, please follow other related articles on the PHP Chinese website!