This article focuses on Java serialization.
If you are interested in Java serialization, you can study it.
1, The role of Java serialization
Sometimes we want to transfer a Java object into a byte stream, and sometimes we want to restore a Java object from a byte stream. For example, sometimes we want to
When we write a Java object to the hard disk or transfer it to other computers on the network, we need to write the corresponding object into a byte stream through Java. For this general
Why don't we use a unified format? Yes, the concept of Java serialization appears here. The subclass ObjectOutput under the OutputStream class of Java-
The Stream class has a corresponding WriteObject(Object object), in which the corresponding object is required to implement the java serialization interface.
In order to better understand the application of java serialization, I give two examples I have encountered in development projects:
1) When using Tomcat to develop Java EE related projects, after we close tomcat, the objects in the corresponding session are stored on the hard disk. If we want to restart tomcat
If the contents in the corresponding session can be read from tomcat, the contents saved in the session must be serialized.
2) If the java object we use is to be used in distributed or rmi remote call network, the relevant object must implement java serialization interface.
Dear friends, you probably know the functions related to java serialization. Next, let's take a look at how to implement java serialization~
2, Realize the serialization and deserialization of java objects.
There are two ways to serialize Java objects.
a. It is the corresponding object that implements the Serializable interface, which is often used. For the Serializable interface, the Serializable interface is an empty interface, and its main function is
This object can be serialized when it is identified, and the jre object will be encapsulated when it is transferred. I won't make too much introduction here.
Here is an example of Java serialization that implements the serialization interface: very simple
12345678
package com.shop.domain; import java.util.Date; public class Article implements java.io.Serializable { private static final long serialVersionUID = 1L; private Integer id; private String title; //Article title private String content; // Article content private String faceIcon;//Emoticon private Date postTime; //Time of publication private String ipAddr; //User's ip private User author; //Users replying public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getFaceIcon() { return faceIcon; } public void setFaceIcon(String faceIcon) { this.faceIcon = faceIcon; } public Date getPostTime() { return postTime; } public void setPostTime(Date postTime) { this.postTime = postTime; } public User getAuthor() { return author; } public void setAuthor(User author) { this.author = author; } public String getIpAddr() { return ipAddr; } public void setIpAddr(String ipAddr) { this.ipAddr = ipAddr; } }
b. the second way to realize serialization is to realize the interface Externalizable. Part of the source code of Externalizable is as follows:
* @see java.io.ObjectInput * @see java.io.Serializable * @since JDK1.1 */ public interface Externalizable extends java.io.Serializable { /** * The object implements the writeExternal method to save its contents * by calling the methods of DataOutput for its primitive values or
First, when serializing objects, this class implements Externalizable Interface, which attributes can be serialized are defined in the writeExternal() method,
What can't be serialized? Therefore, the serialized objects that can be serialized are saved in the file. Those that can't be serialized are not processed, and then they are automatically called when deserializing
Use the readExternal() method to read the reverse sequence one by one according to the sequence order, and automatically encapsulate it into an object return, and then receive it in the test class to complete the reverse sequence.
Therefore, exterminable is an extension of Serializable.
In order to better understand the relevant contents, please see the following examples:
package com.xiaohao.test; import java.io.Externalizable; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.text.SimpleDateFormat; import java.util.Date; /** * Test entity class * @author Xiao Hao * @Creation date: March 12, 2015 */ class Person implements Externalizable{ private static final long serialVersionUID = 1L; String userName; String password; String age; public Person(String userName, String password, String age) { super(); this.userName = userName; this.password = password; this.age = age; } public Person() { super(); } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } /** * Extension class for serialization operation */ @Override public void writeExternal(ObjectOutput out) throws IOException { //Add a new object Date date=new Date(); out.writeObject(userName); out.writeObject(password); out.writeObject(date); } /** * Deserialized extension class */ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { //Note that the order of acceptance here is limited. Otherwise, an error will occur // For example, if the object A is written first above, then the object A must be accepted first below userName=(String) in.readObject(); password=(String) in.readObject(); SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); Date date=(Date)in.readObject(); System.out.println("The date after deserialization is:"+sdf.format(date)); } @Override public String toString() { //Note that the age here will not be serialized, so the data cannot be read during deserialization return "user name:"+userName+"password:"+password+"Age:"+age; } } /** * Serialization and deserialization related operation classes * @ author Xiaohao * @ creation date: March 12, 2015 */ class Operate{ /** * Serialization method * @ throws IOException * @throws FileNotFoundException */ public void serializable(Person person) throws FileNotFoundException, IOException{ ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("a.txt")); outputStream.writeObject(person); } /** * Deserialization method * @ throws IOException * @throws FileNotFoundException * @throws ClassNotFoundException */ public Person deSerializable() throws FileNotFoundException, IOException, ClassNotFoundException{ ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt")); return (Person) ois.readObject(); } } /** * Test entity main class * @ author Xiaohao * @ creation date: March 12, 2015 */ public class Test{ public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { Operate operate=new Operate(); Person person=new Person("Xiao Hao","123456","20"); System.out.println("The relevant data before serialization is as follows:\n"+person.toString()); operate.serializable(person); Person newPerson=operate.deSerializable(); System.out.println("-------------------------------------------------------"); System.out.println("The relevant data after serialization is as follows:\n"+newPerson.toString()); } }
First, when serializing the UserInfo object, since this class implements the Externalizable interface, what attributes are defined in the writeExternal() method
To serialize, which can not be serialized. Therefore, the objects that can be serialized will be saved in the file after passing through. Those that cannot be serialized will not be processed, and then in the deserialization
The readExternal() method is automatically called to read the sequence one by one, reverse the sequence, and automatically encapsulate it into an object return, and then receive it in the test class to complete the reverse sequence.
*** For the implementation of Java serialization interface, you should pay attention to the following points:
1. When serializing in JAVA, transient variables (the keyword is used to tell JAVA that I cannot be serialized) and static variables will not be serialized (the following is a test example)
import java.io.*; class Student1 implements Serializable { private static final long serialVersionUID = 1L; private String name; private transient String password; private static int count = 0; public Student1(String name, String password) { System.out.println("call Student Construction method with parameters"); this.name = name; this.password = password; count++; } public String toString() { return "Number of people: " + count + " full name: " + name + " password: " + password; } } public class ObjectSerTest1 { public static void main(String args[]) { try { FileOutputStream fos = new FileOutputStream("test.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); Student1 s1 = new Student1("Zhang San", "12345"); Student1 s2 = new Student1("Wang Wu", "54321"); oos.writeObject(s1); oos.writeObject(s2); oos.close(); FileInputStream fis = new FileInputStream("test.obj"); ObjectInputStream ois = new ObjectInputStream(fis); Student1 s3 = (Student1) ois.readObject(); Student1 s4 = (Student1) ois.readObject(); System.out.println(s3); System.out.println(s4); ois.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e1) { e1.printStackTrace(); } } }
1234567891011121314151617181920212223242526272829303132333435363738394041424344
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Test{ public static void main(String args[]){ try { FileInputStream fis = new FileInputStream("test.obj"); ObjectInputStream ois = new ObjectInputStream(fis); Student1 s3 = (Student1) ois.readObject(); Student1 s4 = (Student1) ois.readObject(); System.out.println(s3); System.out.println(s4); ois.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e1) { e1.printStackTrace(); } } }
2. It should also be noted that if you serialize object A and then serialize object B, you must remember the objects read first according to JAVA during deserialization
It is the object to be serialized first. Do not receive object B first, which will report an error. Especially when using the above Externalizable, you must pay attention to reading
Order of.
3. The object implementing the serialization interface is not forced to declare a unique serialVersionUID. Whether to declare the serialVersionUID for object serialization
Top down compatibility has a great impact. Let's do a test:
Train of thought I
Remove the serialVersionUID from the User and save it serially. During deserialization, add or reduce fields to see whether it is successful.
Java code
public class User implements Serializable{ private String name; private int age; private long phone; private List<UserVo> friends; ...<br> }
Save to file:
Java code ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(bos); os.writeObject(src); os.flush(); os.close(); byte[] b = bos.toByteArray(); bos.close(); FileOutputStream fos = new FileOutputStream(dataFile); fos.write(b); fos.close();
After adding or reducing fields, read them from the file and deserialize them:
Java code ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(bos); os.writeObject(src); os.flush(); os.close(); byte[] b = bos.toByteArray(); bos.close(); FileOutputStream fos = new FileOutputStream(dataFile); fos.write(b); fos.close();
Result: exception information is thrown
Java code
Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 3305402508581390189, local class serialVersionUID = 7174371419787432394 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350) at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74) at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)
Train of thought II
eclipse specifies to generate a serialVersionUID, serialize and save it, and deserialize it after modifying the field
Omit code
Result: deserialization succeeded
conclusion
If the serialVersionUID is not explicitly specified, a serialVersionUID will be generated according to the field and specific algorithm during serialization. When the attribute changes, the id changes, so during deserialization
Will fail. Throw "the unique id of the local classd does not match the unique id of the class in the stream".
Description of serialVersionUID in jdk document:
Write
If a serializable class does not explicitly declare a serialVersionUID, the serialization runtime will calculate the default serialVersionUID value of the class based on all aspects of the class, as described in the Java(TM) object serialization specification. However, it is strongly recommended that all serializable classes explicitly declare the serialVersionUID value because calculating the default serialVersionUID is highly sensitive to the details of the class and may vary widely according to the compiler implementation, which may lead to unexpected InvalidClassException during deserialization. Therefore, in order to ensure the consistency of serialVersionUID values across different java compiler implementations, the serialization class must declare an explicit serialVersionUID value. It is also strongly recommended to use the private modifier to display the declaration of serialVersionUID (if possible), because this declaration applies only to directly declaring classes – the serialVersionUID field is not useful as an inherited member. Array classes cannot declare an explicit serialVersionUID, so they always have a default calculated value, but array classes have no requirement to match the serialVersionUID value.
3, Other ways to realize serialization (this is an extended content, which can be extended if you are interested)
1) Is to wrap the object into a JSON string transmission.
The JSON format is used here, and Google's gson-2.2.2.jar is used at the same time Escape
2) Using Google's ProtoBuf
With the open source of Google tool protobuf, protobuf is also a good choice. For JSON,Object Serialize (serialization and deserialization of Java),
ProtoBuf Make a comparison.
Define a general object UserVo to be transferred:
public class User <span style="white-space: pre;">private static final long serialVersionUID = -5726374138698742258L;</span> { private String name; private int age; private long phone; private List<User> friends; ...set and get method }
Initialize User instance src:
User user1 = new UserVo(); user1 .setName("user1 "); user1 .setAge(30); user1 .setPhone(13789126278L); UserVo f1 = new UserVo(); f1.setName("tmac"); f1.setAge(32); f1.setPhone(123L); User user2 = new User(); user2 .setName("user2 "); user2 .setAge(29); user2 .setPhone(123L); <br> List<User> friends = new ArrayList<User>(); friends.add(user1 ); friends.add(user2 ); user1 .setFriends(friends);
1. First, use JOSN to realize serialization.
Gson gson = new Gson();<br>String json = gson.toJson(src);
Obtained string:
{"name":"user1 ","age":30,"phone":123,"friends":[{"name":"user1 ","age":32,"phone":123},{"name":"user2 ","age":29,"phone":123}]}
The number of bytes is 153
Jason's advantages: the plaintext structure is clear at a glance, can cross languages, and the increase and decrease of attributes have little impact on the parsing end. Disadvantages: too many bytes, depending on different third-party class libraries.
Object Serialize (serialization and deserialization of Java)
UserVo implements the Serializalbe interface and provides a unique version number:
Serialization method:
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(bos); os.writeObject(src); os.flush(); os.close(); byte[] b = bos.toByteArray(); bos.close();
Deserialization:
ObjectInputStream ois = new ObjectInputStream(fis); vo = (UserVo) ois.readObject(); ois.close(); fis.close();
Advantages of Object Serializalbe: java Native support, no need to provide a third-party class library, and it is relatively simple to use.
Disadvantages: it cannot cross language, occupies a large number of bytes, and is sensitive to changes in object attributes in some cases.
When serializing and deserializing objects, you must implement the Serializable interface, but you are not forced to declare a unique serialVersionUID
Whether to declare serialVersionUID has a great impact on the up-down compatibility of object serialization.
Google ProtoBuf
protocol buffers is an internal transmission protocol of google. At present, the project has been open source( http://code.google.com/p/protobuf/).
It defines a compact and extensible binary protocol format, which is suitable for network transmission, and has different versions for multiple languages.
Take protobuf-2.5.0rc1 as an example, preparation:
Download the source code, extract, compile and install
Shell code
tar zxvf protobuf-2.5.0rc1.tar.gz ./configure ./make ./make install
Test:
Shell code
MacBook-Air:~ ming$ protoc –version libprotoc 2.5.0
Installation succeeded!
Enter the Java directory of the source code, compile and generate the required jar package with mvn tool, protobuf-java-2.5.0rc1.jar
1. Write a. Proto file and name it UserVo.proto
package serialize; option java_package = "serialize"; option java_outer_classname="UserVoProtos"; message User{ optional string name = 1; optional int32 age = 2; optional int64 phone = 3; repeated serialize.UserVo friends = 4; }
2. Use the protoc ol tool to generate the builder class on the command line
Shell code
protoc -IPATH=.proto file directory – java_out=java file output path. proto name
Get UserProtos class
3. Writing serialization code
UserVoProtos.User.Builder builder = UserVoProtos.User.newBuilder(); builder.setName("Yaoming"); builder.setAge(30); builder.setPhone(13789878978L); UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder(); builder1.setName("tmac"); builder1.setAge(32); builder1.setPhone(138999898989L); UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder(); builder2.setName("liuwei"); builder2.setAge(29); builder2.setPhone(138999899989L); builder.addFriends(builder1); builder.addFriends(builder2); UserVoProtos.UserVo vo = builder.build(); byte[] v = vo.toByteArray();
Number of bytes 53
Deserialization
UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb); System.out.println(uvo.getFriends(0).getName());
Result: tmac, deserialization succeeded
Advantages of google protobuf: the number of bytes is very small, suitable for network transmission, saving io and cross language.
Disadvantages: you need to rely on tools to generate code.
Working mechanism
The proto file is a description of data, including field name, type and position in bytes. The protoc tool reads the proto file and generates a class library corresponding to the builder code. protoc xxxxx – java_out=xxxxxx generates a Java class library. The builder class serializes the data into a byte stream according to its own algorithm, or reverses the byte stream into an object according to the principle of reflection. The official example is Example: https://developers.google.com/protocol-buffers/docs/javatutorial.
Correspondence between field types in proto file and java:
See: https://developers.google.com/protocol-buffers/docs/proto
.proto Type java Type c++ Type
double double double
float float float
int32 int int32
int64 long int64
uint32 int uint32
unint64 long uint64
sint32 int int32
sint64 long int64
fixed32 int uint32
fixed64 long uint64
sfixed32 int int32
sfixed64 long int64
bool boolean bool
string String string
bytes byte string
Description of field properties:
Write
required: a well-formed message must have exactly one of this field. optional: a well-formed message can have zero or one of this field (but not more than one). repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.
When serializing and deserializing protobuf, it depends on the builder class generated by the. proto file. If the field change is not reflected in the. proto file, it will not affect the deserialization, which is more suitable for the field change.
Do a test: serialize UserVo into a file:
UserProtos.User vo = builder.build(); byte[] v = vo.toByteArray(); FileOutputStream fos = new FileOutputStream(dataFile); fos.write(vo.toByteArray()); fos.close(); by User Add field, corresponding to.proto File: Text code package serialize; option java_package = "serialize"; option java_outer_classname="UserVoProtos"; message User{ optional string name = 1; optional int32 age = 2; optional int64 phone = 3; repeated serialize.UserVo friends = 4; optional string address = 5; } Deserialize from file: Java code FileInputStream fis = new FileInputStream(dataFile); byte[] dstb = new byte[fis.available()]; for(int i=0;i<dstb.length;i++){ dstb[i] = (byte)fis.read(); } fis.close(); UserProtos.User uvo = UserProtos.User.parseFrom(dstb); System.out.println(uvo.getFriends(0).getName());
12345678910111213141516171819202122232425262728293031
Deserialize from file:
Java code
FileInputStream fis = new FileInputStream(dataFile); byte[] dstb = new byte[fis.available()]; for(int i=0;i<dstb.length;i++){ dstb[i] = (byte)fis.read(); } fis.close(); UserProtos.User uvo = UserProtos.User.parseFrom(dstb); System.out.println(uvo.getFriends(0).getName());
12345
Successful results.
Compared with the three methods of transmitting the same data, google protobuf has only 53 bytes, which is the least. Conclusion:
Advantages and disadvantages of this method
JSON
Cross language and clear format at a glance
The number of bytes is relatively large, and a third-party class library is required
The Object Serialize java Native method does not rely on external class libraries. The number of bytes is large and cannot cross language
Google protobuf
Cross language, less bytes
Write. proto configuration and generate corresponding code with protoc tool