Control of Java serialization

Source: Internet
Author: User
Tags constructor documentation serialization tostring

As you can see, the default serialization mechanism is not difficult to manipulate. But what if there are special requirements? We may have special security issues, we don't want to serialize a part of the object, or a child object does not have to be serialized at all, because after the object is restored, that part needs to be recreated.
At this point, the specific process of serialization can be controlled by implementing the Externalizable interface instead of the serializable interface. This externalizable interface extends the serializable and adds two methods: Writeexternal () and readexternal (). Both methods are invoked automatically during serialization and reassemble so that we can perform some special operations.
The following example shows a simple application of the Externalizable interface method. Note that Blip1 and Blip2 are almost identical, except for very small differences (look at the code yourself to see if it can be found):
 

: Blips.java//simple use of externalizable & a pitfall import java.io.*;

Import java.util.*;
  Class Blip1 implements Externalizable {public Blip1 () {System.out.println ("Blip1 constructor");
  public void Writeexternal (ObjectOutput out) throws IOException {System.out.println ("blip1.writeexternal"); } public void Readexternal (ObjectInput in) throws IOException, ClassNotFoundException {System.out.println ("B
  Lip1.readexternal ");
  Class Blip2 implements Externalizable {Blip2 () {System.out.println ("Blip2 constructor");
  public void Writeexternal (ObjectOutput out) throws IOException {System.out.println ("blip2.writeexternal"); } public void Readexternal (ObjectInput in) throws IOException, ClassNotFoundException {System.out.println ("B
  Lip2.readexternal ");
    } public class Blips {public static void main (string[] args) {System.out.println ("Constructing objects:");
    BLIP1 B1 = new Blip1 (); BliP2 b2 = new Blip2 ();
      try {objectoutputstream o = new ObjectOutputStream (New FileOutputStream ("Blips.out"));
      System.out.println ("Saving objects:");
      O.writeobject (B1);
      O.writeobject (B2);
      O.close (); Now get them back:objectinputstream in = new ObjectInputStream (New FileInputStream ("Blips.out
      "));
      System.out.println ("Recovering B1:");
      B1 = (BLIP1) in.readobject (); oops!   Throws an exception://!
System.out.println ("Recovering B2:");   //!
    B2 = (BLIP2) in.readobject ();
    catch (Exception e) {e.printstacktrace (); }
  }
} ///:~


The program output is as follows:

Constructing objects:
Blip1 constructor Blip2 constructor saving objects
:
blip1.writeexternal
Blip2.writeexternal
recovering B1:
Blip1 constructor
blip1.readexternal


The Blip2 object was not recovered because doing so would result in an offence. Did you find out the difference between Blip1 and Blip2? The Blip1 Builder is "public", and the Blip2 builder does not, which causes the violation to occur at the time of recovery. Try changing the Blip2 Builder property to "public" and then deleting the//! annotation tag to see if you can get the correct result.
When B1 is restored, the BLIP1 default builder is invoked. This is different from restoring a serializable (serializable) object. In the latter case, the object is completely restored based on the bits it has saved, and there is no builder call. For a Externalizable object, all normal default build behavior occurs (including initialization when the field is defined), and readexternal () is invoked. Attention must be paid to this fact-paying special attention to all default build behavior-otherwise it is difficult to generate the correct behavior in your own Externalizable object.
The following example reveals all the things you must do to save and recover a Externalizable object:

: Blip3.java//Reconstructing an Externalizable object import java.io.*;

Import java.util.*;
  Class BLIP3 implements externalizable {int i; String s;
    No initialization Public Blip3 () {System.out.println ("Blip3 constructor");
    s, I not initialized} public Blip3 (string x, int a) {System.out.println ("BLIP3 (string x, int a)");
    s = x;
    i = A;
  S & I initialized only in Non-default//constructor.
  Public String toString () {return s + i;}
    public void Writeexternal (ObjectOutput out) throws IOException {System.out.println ("blip3.writeexternal"); You are must do this:out.writeObject (s);
  Out.writeint (i); } public void Readexternal (ObjectInput in) throws IOException, ClassNotFoundException {System.out.println ("Bli
    P3.readexternal "); 
    You are must do this:s = (String) in.readobject ();
  I =in.readint (); public static void Main (string[] args) {System.out.println ("constructing objects:");
    BLIP3 B3 = new BLIP3 ("A String", 47);
    System.out.println (B3.tostring ());
      try {objectoutputstream o = new ObjectOutputStream (New FileOutputStream ("Blip3.out"));
      System.out.println ("Saving object:");
      O.writeobject (B3);
      O.close (); Now get it back:objectinputstream in = new ObjectInputStream (New FileInputStream ("Blip3.out")
      );
      System.out.println ("Recovering B3:");
      B3 = (BLIP3) in.readobject ();
    System.out.println (B3.tostring ());
    catch (Exception e) {e.printstacktrace (); }
  }
} ///:~

Where the fields S and I are initialized only in the second builder, not the default builder. This means that if s and I are not initialized in readexternal, they become null (because the object's storage space has been purged to 1 in the first step of the object creation). If you comment out two lines of code following the "You must doing this" and run the program, you will find that when the object is restored, S is null, and I is zero.
If you inherit from a Externalizable object, you typically invoke the underlying class version of Writeexternal () and readexternal () to properly save and restore the underlying class components.
So in order for everything to work, you must not write important data for an object only during the execution of the Writeexternal () method (no default behavior can be used to write all member objects for a Externalizable object). Instead, the data must be recovered in the Readexternal () method. The initial operation may be somewhat unaccustomed, because the default build behavior of the Externalizable object makes it appear that some sort of storage and recovery operation is in progress. But the truth is not so.

1. Transient (temporary) keyword
Control the serialization process, there may be a specific child object that is unwilling to allow the Java serialization mechanism to be automatically saved and restored. Generally, this is the case if the child object contains sensitive information, such as a password, that you do not want to serialize. Even if that information has a "private" attribute in the object, once serialized, people can get it by reading a file or intercepting the network transmission.
to prevent the sensitive part of an object from being serialized, one way is to implement its own class as externalizable, as shown earlier. As a result, nothing can be automatically serialized, only Writeexternal () explicitly serialize the parts that are needed.
However, if you are working with a serializable object, all serialization operations will be performed automatically. To solve this problem, you can use transient (ad hoc) to turn off serialization by field, which means "do not bother you (automatic mechanism) to save or restore it-I will handle it myself".
For example, suppose a Login object contains information related to a particular logon session. When verifying the legality of a login, you typically want to save the data, but not the password. The easiest way to do this is to implement serializable and set the password field to transient. The following is the specific code:
 

: Logon.java//demonstrates the "transient" keyword import java.io.*;

Import java.util.*;
  Class Logon implements Serializable {Private date = new Date ();
  Private String username;
  private transient String password;
    Logon (string name, string pwd) {username = name;
  Password = pwd; public string toString () {string pwd = (password = = null)?
    "(N/a)": password;  return logon info: \ n "+" Username: "+ username +" \ n Date: "+ date.tostring () +" \ n Password:
  "+ pwd;
    public static void Main (string[] args {Logon a = new logon ("Hulk", "mylittlepony");
    SYSTEM.OUT.PRINTLN ("Logon a =" + a);
      try {objectoutputstream o = new ObjectOutputStream (New FileOutputStream ("Logon.out"));
      O.writeobject (a);
      O.close ();
      Delay:int seconds = 5;
      Long T = System.currenttimemillis () + seconds * 1000;
    while (System.currenttimemillis () < T)    ; Now get them back:objectinputstream in = new ObjectInputStream (New FileInputStream ("Logon.out
      "));
      System.out.println ("Recovering object at" + New Date ());
      A = (Logon) in.readobject ();
    SYSTEM.OUT.PRINTLN ("Logon a =" + a);
    catch (Exception e) {e.printstacktrace (); }
  }
} ///:~

As you can see, the date and Username fields remain in their original state (not set to transient), so they are automatically serialized. However, password is set to transient, so it is not automatically saved to disk, and the automatic serialization mechanism does not attempt to recover it. The output is as follows:

Logon A = Logon info:
   username:hulk
   Date:sun Mar 18:25:53 PST 1997
   Password:mylittlepony recovering
Object at Sun Mar 18:25:59 PST 1997
Logon A = Logon info:
   username:hulk
   date:sun Mar 18:25:53 PST 1997
   Password: (n/a)

Once the object reverts to its original form, the password field becomes null. Note You must use ToString () to check that password is null because if you assemble a string object with an overloaded "+" operator, and that operator encounters a null handle, Will cause a violation called NullPointerException (the new version of Java may provide code to avoid this problem).
We also found that the date field was saved to disk and recovered from disk without rebuilding.
Because the Externalizable object does not save any of its fields by default, the Transient keyword can only be used with serializable.

2. Alternative methods of externalizable
There's another way to do it if you don't care about implementing the Externalizable interface. We can implement the Serializable interface and add (note "Add" rather than "overwrite" or "implement") the method named WriteObject () and ReadObject (). Once the object is serialized or reassembled, the two methods are called separately. That is, as long as these two methods are provided, they are used preferentially, regardless of the default serialization mechanism.
These methods must contain the following exact signatures:

 private void WriteObject (ObjectOutputStream stream) throws IOException; private void ReadObject (ObjectInputStream stream) throws IOException, ClassNotFoundException 

From a design point of view, the situation becomes somewhat confusing. First, you might think that these methods are not part of the underlying class or serializable interface, and they should be defined in their own interfaces. Note, however, that they are defined as "private," which means that they can only be invoked by other members of the class. However, we do not actually invoke them from other members of this class, but by ObjectOutputStream and ObjectInputStream's WriteObject () and ReadObject () method to invoke the WriteObject () and ReadObject () Methods of our objects (note that I use a lot of inhibition here to avoid using the same method name-for fear of confusion). You may wonder how ObjectOutputStream and ObjectInputStream have access to the private methods of our class--only that this is a trick played by the serialization mechanism.
In any case, anything defined in the interface automatically has the public property, so if WriteObject () and readobject () must be private, they cannot be part of the interface (interface). But because we add the signature accurately, the final effect is actually the same as implementing an interface.
It seems that when we call Objectoutputstream.writeobject (), the serializable object we pass to it appears to be checked to see if it implements its own writeobject (). If the answer is yes, the regular serialization process is skipped and the WriteObject () is invoked. ReadObject () will also encounter the same situation. There is another problem with the
. Within our writeobject (), you can call Defaultwriteobject () and decide to take the default writeobject () action. Similarly, within readobject (), you can call Defaultreadobject (). The following simple example demonstrates how to control the storage and recovery of a serializable object:
 

: Serialctl.java//controlling serialization by adding your own//WriteObject () and ReadObject () methods.

Import java.io.*;
  public class Serialctl implements Serializable {String A;
  transient String b;
    Public Serialctl (String aa, String bb) {a = ' not Transient: ' + AA;
  b = "Transient:" + bb;
  Public String toString () {return a + "\ n" + B;
    private void WriteObject (ObjectOutputStream stream) throws IOException {Stream.defaultwriteobject ();
  Stream.writeobject (b); } private void ReadObject (ObjectInputStream stream) throws IOException, ClassNotFoundException {STREAM.D
    Efaultreadobject ();
  b = (String) stream.readobject ();
    public static void Main (string[] args) {Serialctl sc = new Serialctl ("Test1", "Test2");
    System.out.println ("before:\n" + SC);
    Bytearrayoutputstream buf = new Bytearrayoutputstream ();
   try {objectoutputstream o = new ObjectOutputStream (BUF);   O.writeobject (SC);
            It back:objectinputstream in = new ObjectInputStream (New Bytearrayinputstream (
      Buf.tobytearray ()));
      Serialctl SC2 = (serialctl) in.readobject ();
    System.out.println ("after:\n" + SC2);
    catch (Exception e) {e.printstacktrace (); }
  }
} ///:~

In this example, a string remains in the original state, and the other is set to transient (temporary) to prove that the Defaultwriteobject () method is automatically saved by the transient field, which must be explicitly saved and restored in the program. Fields are initialized inside the builder, not at the time of the definition, which proves that they are not initialized by some automated mechanisms when they are reassembled.
If you are ready to write to the transient part of an object through the default mechanism, you must call Defaultwriteobject () as the first action in WriteObject (), and call Defaultreadobject (). Make it the first action for ReadObject (). These are uncommon calling methods. For example, when we call Defaultwriteobject () for a objectoutputstream, and we do not pass arguments for it, we need to take this action so that it knows the handle of the object and how to write all the parts that are not transient. This approach is very inconvenient. The
Transient object is stored and restored with code that we are more familiar with. Now consider what will happen. A Serialctl object is created in main () and then serialized into a objectoutputstream (note that in this case a buffer is used rather than a file-exactly the same as ObjectOutputStream). The formal serialization takes place in the following line of code:
O.writeobject (SC);
where the WriteObject () method must check the SC to determine whether it has its own writeobject () method (not to check its interface- It is not at all, it is not the type of checking class, but the actual search method using reflection method. If the answer is yes, use that method. A similar situation can occur on readobject (). Perhaps this is the only practical way to solve the problem, but it does seem odd.

3. Version problems
Sometimes you might want to change the version of a serializable class (for example, objects of the original class might be saved in the database). While this approach has been supported, it should generally be used only in very special circumstances. In addition, it requires the operator to have a deeper understanding of the underlying principles, and we do not want to reach this depth here. The HTML documentation for JDK 1.1 is a very comprehensive discussion of this topic (downloadable from sun, but may also be part of the Java Development Package online documentation).

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.