: Notebook: This article is filed under “blog”
: Keyboard: The sample code in this article is archived under: “Javacore”
Introduction to the
- Serialize – Serialize converts an object to a byte stream.
- Deserialize – Deserialize is to convert a byte stream into an object.
- Serialization purpose
- Serialization can persist an object’s byte sequence — in memory, in a file, in a database.
- Transmits the byte sequence of an object over the network.
- RMI(Remote method Invocation)
Note: With Java object serialization, when an object is saved, its state is saved as a set of bytes that will be assembled into objects in the future. It is important to note that object serialization holds the object’s “state,” its member variables. Thus, object serialization does not care about static variables in a class.
Serialization and deserialization
Java implements serialization and deserialization through object input and output streams:
java.io.ObjectOutputStream
Of the classwriteObject()
Method can implement serialization;java.io.ObjectInputStream
Of the classreadObject()
The deserialization method is used to implement deserialization.
Examples of serialization and deserialization:
public class SerializeDemo01 {
enum Sex {
MALE,
FEMALE
}
static class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name = null;
private Integer age = null;
private Sex sex;
public Person(a) {}public Person(String name, Integer age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString(a) {
return "Person{" + "name='" + name + '\' ' + ", age=" + age + ", sex=" + sex + '} '; }}/** * serialize */
private static void serialize(String filename) throws IOException {
File f = new File(filename); // Define the save path
OutputStream out = new FileOutputStream(f); // File output stream
ObjectOutputStream oos = new ObjectOutputStream(out); // Object output stream
oos.writeObject(new Person("Jack".30, Sex.MALE)); // Save the object
oos.close();
out.close();
}
/** * deserialize */
private static void deserialize(String filename) throws IOException, ClassNotFoundException {
File f = new File(filename); // Define the save path
InputStream in = new FileInputStream(f); // File input stream
ObjectInputStream ois = new ObjectInputStream(in); // Object input stream
Object obj = ois.readObject(); // Read the object
ois.close();
in.close();
System.out.println(obj);
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
final String filename = "d:/text.dat"; serialize(filename); deserialize(filename); }}// Output:
// Person{name='Jack', age=30, sex=MALE}
Copy the code
The Serializable interface
The serialized class must belong to any of the Enum, Array, and Serializable types.
If serialization is required, the java.io.Serializable interface must be implemented. Otherwise, NotSerializableException will be thrown. This is because the type is checked during the serialization operation and an exception is thrown if the serialization type requirements are not met.
Let’s try a little: change the Person class in the SerializeDemo01 example to the following implementation, and see what happens.
public class UnSerializeDemo {
static class Person { // Other content omitted}
// Other content omitted
}
Copy the code
Output: The result is the following exception information.
Exception in thread "main" java.io.NotSerializableException:
...
Copy the code
serialVersionUID
Note the serialVersionUID field, which you can see in countless classes in the Java world.
What does serialVersionUID do, and how do I use it?
SerialVersionUID is the version identifier Java generates for each serialized class. It can be used to ensure that the sender sends and the receiver receives compatible objects in antisequence. If the serialVersionUID of the class received by the receiver is not the same as the serialVersionUID sent by the sender, InvalidClassException is thrown.
If a serializable class does not explicitly declare a serialVersionUID, the serializable runtime calculates the default serialVersionUID value for the class based on aspects of the class. However, it is recommended that you explicitly specify the value of serialVersionUID in each serialized class. Because different JDK compilations are likely to generate different serialVersionUID defaults, InvalidClassExceptions will be thrown during deserialization.
The serialVersionUID field must be of static Final Long type.
Let’s take an example:
(1) There is a serializable class Person
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
private String address;
// the constructor, get and set methods are omitted
}
Copy the code
(2) During the development process, Person was modified and a field email was added as follows:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
private String address;
private String email;
// the constructor, get and set methods are omitted
}
Copy the code
Since this class is incompatible with the older version, we need to change the version number:
private static final long serialVersionUID = 2L;
Copy the code
Deserialization again throws an InvalidClassException.
From the above, it’s probably clear that serialVersionUID is used to control whether serialized versions are compatible. We do not change the serialVersionUID if we think the serializable class we are modifying is backward compatible.
Default serialization mechanism
If you simply let a class implement the Serializable interface without doing anything else, the default serialization mechanism is used.
Using the default mechanism, when you serialize an object, you serialize not only the current object itself, but also the fields of its parent class and other objects referenced by the object. Similarly, other objects referenced by these other objects will be serialized, and so on. Therefore, if an object contains member variables that are container-like objects, and the elements that these containers contain are container-like objects, the serialization process can be complicated and expensive.
Note: Since the parent class and reference object are to be serialized, they must also satisfy the serialization requirements: the serialized class must belong to any of the Enum, Array, and Serializable types.
Non-default serialization mechanism
In real-world applications, there are times when the default serialization mechanism cannot be used. For example, you might want to ignore sensitive data during serialization, or you might want to simplify the serialization process. Several ways to affect serialization are described below.
Transient keyword
When a field is declared transient, it is ignored by the default serialization mechanism.
We declare the age field of the inner class Person in the SerializeDemo01 example transient as follows:
public class SerializeDemo02 {
static class Person implements Serializable {
transient private Integer age = null;
// Other content omitted
}
// Other content omitted
}
// Output:
// name: Jack, age: null, sex: MALE
Copy the code
As you can see from the output, the AGE field was not serialized.
Externalizable interface
Both the TRANSIENT keyword and the writeObject() and readObject() methods are based on Serializable interface serialization.
Another serialization interface, Externalizable, is provided in the JDK.
After a Serializable class implements the Externalizable interface, the default serialization mechanism based on the Serializable interface becomes invalid.
Let’s make some more changes based on SerializeDemo02, as follows:
public class ExternalizeDemo01 {
static class Person implements Externalizable {
transient private Integer age = null;
// Other content omitted
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {}@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}}// Other content omitted
}
// Output:
// call Person()
// name: null, age: null, sex: null
Copy the code
From this result, on the one hand, you can see that none of the fields in the Person object have been serialized. On the other hand, if you are careful, you can see that this serialization procedure calls the no-argument constructor of the Person class.
Externalizable
Inheritance inSerializable
, it adds two methods:writeExternal()
与readExternal()
. These methods are called automatically during serialization and deserialization to perform special operations. When using this interface, the serialization details are left up to the programmer. The code shown above, due towriteExternal()
与readExternal()
Method does nothing, then the serialization behavior will not save/read any fields. This is why all fields in the output are empty.- In addition,With Externalizable serialization, when an object is read, the no-argument constructor of the serialized class is called to create a new object. The values of the fields of the saved object are then filled into the new object. This is why the no-argument constructor of the Person class is called during this serialization. For this reason, the implementation
Externalizable
The class of the interface must provide a constructor that takes no arguments and has access topublic
.
Further modify the Person class above to serialize the name and age fields, but ignore the gender fields, as shown in the code below:
public class ExternalizeDemo02 {
static class Person implements Externalizable {
transient private Integer age = null;
// Other content omitted
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); age = in.readInt(); }}// Other content omitted
}
// Output:
// call Person()
// name: Jack, age: 30, sex: null
Copy the code
An alternative to the Externalizable interface
Implementing the Externalizable interface controls the details of serialization and deserialization. It has an alternative: implement the Serializable interface and add writeObject(ObjectOutputStream Out) and readObject(ObjectInputStream in) methods. Both methods are called back automatically during serialization and deserialization.
Here is an example:
public class SerializeDemo03 {
static class Person implements Serializable {
transient private Integer age = null;
// Other content omitted
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
// Other content omitted
}
// Other content omitted
}
// Output:
// name: Jack, age: 30, sex: MALE
Copy the code
DefaultWriteObject () in ObjectOutputStream is first called in the writeObject() method, which implements the default serialization mechanism as described in the previous section, ignoring the age field. The writeInt() method is then called to explicitly write the age field to the ObjectOutputStream. ReadObject () reads objects in the same way as writeObject().
Note: writeObject() and readObject() are both private methods, so how are they called? Using reflection, no doubt. For details, see the writeSerialData method in ObjectOutputStream and the readSerialData method in ObjectInputStream.
ReadResolve () method
When we use the Singleton pattern, we should expect instances of a class to be unique, but if the class is serializable, the situation may be slightly different. At this point, modify the Person class used in Section 2 to implement the Singleton pattern as follows:
public class SerializeDemo04 {
enum Sex {
MALE, FEMALE
}
static class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name = null;
transient private Integer age = null;
private Sex sex;
static final Person instatnce = new Person("Tom".31, Sex.MALE);
private Person(a) {
System.out.println("call Person()");
}
private Person(String name, Integer age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public static Person getInstance(a) {
return instatnce;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
public String toString(a) {
return "name: " + this.name + ", age: " + this.age + ", sex: " + this.sex; }}/** * serialize */
private static void serialize(String filename) throws IOException {
File f = new File(filename); // Define the save path
OutputStream out = new FileOutputStream(f); // File output stream
ObjectOutputStream oos = new ObjectOutputStream(out); // Object output stream
oos.writeObject(new Person("Jack".30, Sex.MALE)); // Save the object
oos.close();
out.close();
}
/** * deserialize */
private static void deserialize(String filename) throws IOException, ClassNotFoundException {
File f = new File(filename); // Define the save path
InputStream in = new FileInputStream(f); // File input stream
ObjectInputStream ois = new ObjectInputStream(in); // Object input stream
Object obj = ois.readObject(); // Read the object
ois.close();
in.close();
System.out.println(obj);
System.out.println(obj == Person.getInstance());
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
final String filename = "d:/text.dat"; serialize(filename); deserialize(filename); }}// Output:
// name: Jack, age: null, sex: MALE
// false
Copy the code
It is worth noting that the Person object retrieved from the file is not equal to the singleton object in the Person class. To preserve the nature of sequences in a singleton class, use the readResolve() method. In this method, a singleton object of Person is returned directly. We add a readObject method based on the SerializeDemo04 example, as follows:
public class SerializeDemo05 {
// Other content omitted
static class Person implements Serializable {
// Add this method
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
age = in.readInt();
}
// Other content omitted
}
// Other content omitted
}
// Output:
// name: Tom, age: 31, sex: MALE
// true
Copy the code
Serialization tool
Java’s official serialization has many problems, so many people prefer to use a good third-party serialization tool instead of Java’s own serialization mechanism.
Java’s official serialization is mainly reflected in the following aspects:
- Java official serialization performance is not high, serialized data compared with some excellent serialization tools, or much larger, which greatly affects the efficiency of storage and transmission.
- Java’s official serialization must implement the Serializable interface.
- Java’s official serialization requires a focus on the serialVersionUID.
- Java official sequences cannot be used across languages.
Of course, we also have some more excellent serialization and deserialization tools, according to different use scenarios can choose!
- Thrift, Protobuf – Applies to internal systems that are sensitive to performance and do not require high development experience.
- Hessian – For internal and external systems that are sensitive to development experience and have performance requirements.
- Jackson, gson, FastJSON – suitable for serialized data requiring good readability (json, XML form).
summary
The resources
- Java Programming ideas
- JAVA Core Technologies (Volume 1)
- www.hollischuang.com/archives/11…
- www.codenuclear.com/serializati…
- www.blogjava.net/jiangshachi…
- Advanced knowledge of Java serialization
- agapple.iteye.com/blog/859052