Default order serialization in J2SE

Source: Internet
Author: User

The object to be saved is also saved. Generally, we only need to save the Logical Data. You can use the key word transient to mark the data that does not need to be saved.

The following is an example:

Import java. io .*;

Public class Serial implements Serializable {
Int company_id;
String company_addr;

Transient boolean company_flag;
}

Then the company_flag field will not participate in serialization and deserialization, but you also add the responsibility for its initial value. This is also one of the common problems caused by serialization. Serialization is equivalent to a public constructor that only accepts data streams. This object constructor is out of the language. However, it is still a form of constructor. If your class cannot be initialized in other ways, you need to provide additional readObject methods. First, the normal deserialization is performed, and then the Fields marked by transient are initialized.

When it is not suitable, using java's default serialization behavior may affect the speed. The worst case is that it may cause overflow. In the implementation of some data structures, various circular references are often filled, while java's default serialization behavior does not understand your object structure, the result is that java tries to save the object State through an expensive "Graph traversal. It is conceivable that it is not only slow but may also overflow. In this case, you must provide your own readObject to replace the default behavior.

Compatibility problems

Compatibility has always been a complex and troublesome issue.

Not compatible:

First, let's take a look at what we should pay attention to if we aim to avoid compatibility. There are many scenarios where there is no compatibility. For example, war3 won't be able to read the previous replays whenever the version is upgraded.

Compatible, that is, Version Control. java uses a UID (stream unique identifier) to control it. This UID is implicit and is calculated by class name, method name, and many other factors, in theory, it is a one-to-one ing relationship, which is unique. If the UID is different, deserialization cannot be implemented and InvalidClassException will be obtained.

When we want to generate a new version (the implementation is not changed) and discard the previous version, we can achieve it through the explicit famous UID:

Private static final long serialVersionUID = ????;

You can create a version number, but do not repeat it. In this way, the old version will get InvalidClassException during deserialization. We can catch this exception in the old version and prompt the user to upgrade the new version.

Maintain compatibility when the changes are not significant (a special case of downward compatibility ):

Sometimes your class adds irrelevant non-private methods, but the logic field does not change, you certainly want the old version and the new version to maintain compatibility, the method is also implemented through an explicit famous UID. Let's verify it.

Old Version:

Import java. io .*;

Public class Serial implements Serializable {

Int company_id;
String company_addr;

Public Serial1 (int company_id, String company_addr ){
This. company_id = company_id;
This. company_addr = company_addr;
}

Public String toString (){
Return "DATA:" + company_id + "" +
Company_addr;
}
}

New Version

Import java. io .*;

Public class Serial implements Serializable {

Int company_id;
String company_addr;
Public Serial1 (int company_id, String company_addr ){
This. company_id = company_id;
This. company_addr = company_addr;
}

Public String toString (){
Return "DATA:" + company_id + "" + company_addr;
}
Public void todo () {}// irrelevant Method
}

First serialize the old version and then read it using the new version. An error occurs:

Java. io. InvalidClassException: Serial. Serial1; local class incompatible: stream classdesc serialVersionUID = 762508508425139227, local class serialVersionUID = 1187169935661445676

Next we will add an explicit famous UID:

Private static final long serialVersionUID = 762508508425139227l;


Re-running, smooth real estate to produce new objects

DATA: 1001 com1

How to maintain upward compatibility:

Upward compatibility means that the old version can read data streams serialized by the new version. We often see data updates on our servers. We still hope that the old client can support deserialization of new data streams until it is updated to the new version. It can be said that this is semi-automated.

Generally speaking, in java, serialVersionUID is the only identifier that controls whether deserialization is successful. If this value is different, deserialization cannot be successful. However, as long as the value is the same, all deserialization will be performed. In this process, excessive content in the new data stream will be ignored for upward compatibility. For downward compatibility, all content contained in the old data stream will be restored, and the content not involved in the new version of the class will be kept by default. With this feature, we can say that as long as we think that the serialVersionUID remains unchanged, the upward compatibility is automatically implemented.

Of course, once we remove the old content from the new version, the situation is different. Even if the UID remains unchanged, an exception will occur. It is precisely because of this that we should keep in mind that once a class achieves serialization and maintains the upward and downward compatibility, it cannot be modified at will !!!

The test also proves this point. Interested readers can try it on their own.
How to maintain downward compatibility:

As mentioned above, you will assume that as long as the serialVersionUID remains unchanged, downward compatibility is automatically implemented. However, backward compatibility is more complex. This is because we must be responsible for those fields that are not initialized. Make sure they are usable.

Therefore, you must use

Private void readObject (java. io. ObjectInputStream in)
Throws IOException, ClassNotFoundException {
In. defaultReadObject (); // deserialize the object first
If (ver = 5552 ){
// Earlier version 5552
... Initialize other fields
} Else if (ver = 5550 ){
// Earlier version 5550
... Initialize other fields
} Else {
// The old version is not supported
Throw new InvalidClassException ();
}
}

Careful readers will notice that to ensure the smooth execution of in. defaultReadObject ();, serialVersionUID must be consistent, so the server here cannot use serialVersionUID. The ver here is a pre-installed final long ver = xxxx; and it cannot be modified by transient. Therefore, there are at least three requirements for downward compatibility:

1. Consistent serialVersionUID

2. Pre-install final long ver = xxxx for our version recognition mark;

3. Ensure that all domains are initialized.

  Discuss the compatibility policy:

Here we can see that it is difficult to maintain downward compatibility. As the number of versions increases. Maintenance becomes difficult and cumbersome. The Compatibility serialization policy for programs that should be used exceeds the scope of this article. However, the disk storage function of a game must have different compatibility requirements for documents of a word processing software. For the storage function of rpg games, it is generally required to be backward compatible. If java serialization is used here, you can make preparations based on the above three points. In this case, the object serialization method can still be used. Documents of a word processing software have high compatibility requirements. In general, the policies require good downward compatibility, and the best upward compatibility. In general, Object serialization technology is not used. A well-designed document structure can better solve the problem.

  Data Consistency and constraints

We need to know That serialization is another form of "public constructor", but it only constructs the object and does not perform any checks. This is very uncomfortable, therefore, the necessary check is required, which uses readObject ()

Private void readObject (java. io. ObjectInputStream in)
Throws IOException, ClassNotFoundException {
In. defaultReadObject (); // deserialize the object first
... Check and initialize
}

For the sake of structuring, a function named initialize is usually used to check and initialize the function. If the function fails, an exception is thrown. It is easy to forget to maintain check and initialization, which often leads to problems. Another problem is that when the parent class is not added to readObject (), it is easy for the subclass to forget to call the corresponding initialize function. This seems to return to the question of why constructor should be introduced at the beginning, because it is to prevent the subclass from failing to call the initialization function and causing various problems. Therefore, to maintain data consistency, you must add readObject ().

Security Questions

The security topic is beyond the scope of this article, but you should be aware that an attacker may prepare a malicious data stream for your class and attempt to generate a wrong class. When you need to ensure the security of your object data, you can generally use the above method to check and initialize, but it is not good for some references. The solution is to make protective copies of important parts. We recommend a good method to copy the entire object without the need to copy individual domains. This is:

Object readResolve () throws ObjectStreamException;

The purpose of this method is that it will be called immediately after readObject. It will replace the original deserialization object with the returned object. That is, the deserialization of the original readObject () will be immediately discarded.

Object readResolve () throws ObjectStreamException {
Return new serial (this. xxx1, this. xxx2); // xxx1 and xxx2 are just deserialized, which is a protective copy.
}

This method is a waste of time, but can be used for important and secure classes. This method can also be used if the data consistency and constraints are solved by checking them one by one. However, you must consider the cost and pay attention to the following limitations. One obvious drawback of using readResolve () is that when the parent class implements readResolve (), the subclass will become useless. If a protected or public parent class's readResolve () exists and the subclass does not rewrite it, The subclass will eventually get a parent class object during deserialization, this is neither a result nor an easy way to find such errors. Rewriting readResolve () by subclass is undoubtedly a burden. That is to say, for the class to be inherited, implementing readResolve () to protect the class is not a good method. We can only write a protective readObject () using the first method ().

So my suggestion is: Generally, readResolve () is only used for final classes for protection.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.