Wednesday, July 22, 2015

Serialization with Externalizable


  • Class implements Externalizable. Class has to implement writeExternal() and readExternal().
  • We have to define our own implementation for serialization and deserialization process. Default implementation is not available(Unlike Serializable).
  • During deserialization default constructor is used. Hence default constructor is must. Otherwise BOOOM.
  • Serialization using Externalizable is little faster than Serializable.
  • Super.writeExternal() needs to be executed for super class field’s serialization.
Example:- 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Student implements Externalizable {
 
 private String name;
 
 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 @Override
 public void writeExternal(ObjectOutput out) throws IOException {
  out.writeObject(name);
 }

 @Override
 public void readExternal(ObjectInput in) throws IOException,
   ClassNotFoundException {
  name = (String) in.readObject();
 }
}


public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  Student student = new Student();
  student.setName("External");
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student_ex.ser"));
  out.writeObject(student);
  out.close();
  System.out.println("Serialization complete");
 }
}


public class Deserialize {
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("student_ex.ser"));
  Student student = (Student) in.readObject();
  in.close();
  System.out.println(student.getName());
 }
}

Serialization


  • Serialization is a process of saving the state of an Object. This is required basically when we need to send data over network or when there are multiple JVMs associated with an application.
  • An object that implements Serializable interface can be serialized. Serialization and deserialization are achieved by ObjectOutpuStream and ObjctInputStream.

From Oracle ObjectOutputStream documentation
  • An ObjectOutputStream writes primitive data types and graphs of Java objects to an OutputStream. The objects can be read (reconstituted) using an ObjectInputStream. 
  • Only objects that support the java.io.Serializable interface can be written to streams. The class of each serializable object is encoded including the class name and signature of the class, the values of the object's fields and arrays, and the closure of any other objects referenced from the initial objects.
  • The method writeObject is used to write an object to the stream.
  •  The objects must be read back from the corresponding ObjectInputstream with the same types and in the same order as they were written.
  • The default serialization mechanism for an object writes the class of the object, the class signature, and the values of all non-transient and non-static fields


Read more on below links


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Student implements Serializable {
 private static final long serialVersionUID = 2344141467672536362L;
 private String name;
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
}


public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  Student student = new Student();
  student.setName("Deva");
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student_deva.ser"));
  out.writeObject(student);
  out.close();
 }

}

public class Deserialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("student_deva.ser"));
  Student student = (Student) in.readObject();
  in.close();
  System.out.println(student.getName());
 }
}

O/P:- Deva

  • Serialization does not use any constructor.
Example:- 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Student implements Serializable {
 private static final long serialVersionUID = 2344141467672536362L;
 private String name;
 public Student(String name) {
  this.name = name;
 }
 public String getName() {
  return name;
 }
}


public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  Student student = new Student("Litu");
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student_litu.ser"));
  out.writeObject(student);
  out.close();
 }
}


public class Deserialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("student_litu.ser"));
  Student student = (Student) in.readObject();
  in.close();
  System.out.println(student.getName());
 }
}

O/p:- Litu


Q) Can we serialize a circular linked list? Since it contains cyclic reference how the serialization happens? 

Ans:- YES

Yes, the default Java serialization works for cyclic references. When you serialize object C, the field will contain a backreference to the already-serialized object A instead of serializing it again.

http://stackoverflow.com/questions/1792501/does-java-serialization-work-for-cyclic-references

serialVersionUID:-

serialVersionUID ensures the same class is used during deserialization. Throws java.io.InvalidClassException during deserialization if serialVersionUID of serialized and deserialized class does not match.

The serialVersionUID is used as a version control in a Serializable class. If you do not explicitly declare a serialVersionUID, JVM will do it for you automatically, based on various aspects of your Serializable class.

It is recommended to always define serialVersionUID.


Q) What if the class structure is changed after serialization and some new attributes is added but the serialVersionUID same. What will happen during desrialization.
Ans:- No error. But the new attributes will get their default value. Same case if some fields are deleted(No error). 

  • If the serialized class is not found during deserialization it throws java.lang.ClassNotFoundException.
  • During serialization all Serializable attributes also get serialized.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Student implements Serializable {
 
 private static final long serialVersionUID = 1L;
 
 private String name;
 private String age; 
 private Address address;
}
public class Address implements Serializable {
 private String city;
}

In above case when we serialize Student Object Address Object also gets serialized.


  •  If the reference Object is not Serializabe then either we have to ignore it (transient) or customize our serialization(by defining writeObject() and readObject()) which will be executed during serialization and deserialization process. Otherwise booom. NotSerializableException.

Q) Can we serialize a non-serializable field by using writeObject()  method?

No:-  From oracle documentation of ObjectOutputStream

 State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.
Serialization does not write out the fields of any object that does not implement the java.io.Serializable interface....

public interface DataOutput

The DataOutput interface provides for converting data from any of the Java primitive types to a series of bytes and writing these bytes to a binary stream. There is also a facility for converting a String into modified UTF-8 format and writing the resulting series of bytes.



writeObject

public final void writeObject(Object obj)
                       throws IOException
Write the specified object to the ObjectOutputStream. The class of the object, the signature of the class, and the values of the non-transient and non-static fields of the class and all of its supertypes are written. Default serialization for a class can be overridden using the writeObject and the readObject methods. Objects referenced by this object are written transitively so that a complete equivalent graph of objects can be reconstructed by an ObjectInputStream.Exceptions are thrown for problems with the OutputStream and for classes that should not be serialized. All exceptions are fatal to the OutputStream, which is left in an indeterminate state, and it is up to the caller to ignore or recover the stream state.
Specified by:
writeObject in interface ObjectOutput
Parameters:
obj - the object to be written
Throws:
InvalidClassException - Something is wrong with a class used by serialization.
NotSerializableException - Some object to be serialized does not implement the java.io.Serializable interface.
IOException - Any exception thrown by the underlying OutputStream.

defaultWriteObject

public void defaultWriteObject()
                        throws IOException
Write the non-static and non-transient fields of the current class to this stream. This may only be called from the writeObject method of the class being serialized. It will throw the NotActiveException if it is called otherwise.
Throws:
IOException - if I/O errors occur while writing to the underlying OutputStream

So from above definition serialization write state of Serilizable object. We can use writeObject() to customize our serialization since default serialization serializes all fields(except transient and static) available in the object state, that does not mean we can serialize non serializable object using writeObject(). However we can serialize transient fields which are serializable using writeObject().

For non-serializable object reference we have to do it manually by writing the fields of non-serializable object reference to the stream in writeObject().


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Student implements Serializable {

 private static final long serialVersionUID = 1958616912221590245L;
 
 private String name;
 private transient Address address;
 
 public Student(String name, Address address) {
  this.name = name;
  this.address = address;
 }

 public String getName() {
  return name;
 }

 public Address getAddress() {
  return address;
 }
 
 private void writeObject(ObjectOutputStream os) throws IOException {
  os.defaultWriteObject();
  os.writeObject(address);
 }
 
 private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
  in.defaultReadObject();
  address = (Address) in.readObject();
 }

}



public class Address {
 
 private String city;
 private String state;

 public Address(String city, String state) {
  this.city = city;
  this.state = state;
 }
 
 public String getCity() {
  return city;
 }

 public String getState() {
  return state;
 }
 
}



public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  
  Address address = new Address("Bangalore", "Karnataka");
  Student student = new Student("Deva", address);
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student_deva.ser"));
  out.writeObject(student);
  out.close();
 }

}

O/P:


Exception in thread "main" java.io.NotSerializableException: writeobjectfornonserializable.Address

at java.io.ObjectOutputStream.writeObject0(Unknown Source)


Q) Can we use another class with same serialVersionId and attributes to deserialize the class. 

Ans:- 

Serialization/Deserialization is an internal mechanism of JVM. When we serialize a object JVM checks few things like if the object is serializable or not etc. During deserialization JVM checks if the class of the serialized object is valid(fully qualified class exists in class path, matches svid, class is serializable etc) then creates an object from the serialized data. There is no way we can ask jvm to use another class during desirealization. Yes we get error when we try to cast to a different class.



ObjectInputStream in = new ObjectInputStream(new FileInputStream("student_lit.ser"));
Student student = (Student) in.readObject();


During deserialization an instance of same class is read. So when we try to cast it to a different class it throws 


Exception in thread "main" java.lang.ClassCastException: withoutmetadataserialization.Student cannot be cast to serialversionid.Student

at withoutmetadataserialization.Deserialize.main(Deserialize.java:11)



Q) What happens when you remove "implements serializable" after the class is serialized? 

Ans:- Exception.  
Exception in thread "main" java.io.InvalidClassException: happypath.Student; class invalid for deserialization




readObject() and writeObject()

Example:-


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class Student implements Serializable {
 
 private static final long serialVersionUID = 3773034256669009240L;
 
 transient private Address address;
 private String name;
 
 public Student(Address address, String name) {
  this.address = address;
  this.name = name;
 }

 public Address getAddress() {
  return address;
 }
 
 public void setAddress(Address address) {
  this.address = address;
 }

 public String getName() {
  return name;
 }
 
 //private is mandatory. Otherwise it wont be invoked by JVM.
 private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
  in.defaultReadObject();
  String city = (String) in.readObject();
  address = new Address();
  address.setCity(city);
 }
 
 private void writeObject(ObjectOutputStream out) throws IOException {
  out.defaultWriteObject();
  out.writeObject(address.getCity());
 }
}


public class Address {
 
 private String city;

 public String getCity() {
  return city;
 }

 public void setCity(String city) {
  this.city = city;
 }
}


public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  
  Address address = new Address();
  address.setCity("Bangalore");
  
  Student student = new Student(address, "lit");
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student_lit.ser"));
  out.writeObject(student);
  out.close();
  System.out.println("Serialization complete");
 }
}


public class Deserialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("student_lit.ser"));
  Student student = (Student) in.readObject();
  in.close();
  System.out.println(student.getName());
  System.out.println(student.getAddress().getCity());
 }
}

O/P:-

lit
Bangalore


VVIMP Note:- 
  1. readObject() and writeObject() has to be private. For any other access specifier these methods will not be called during serialization and deserialization process.
  2. The serialization and deserialization order is same.

During deserialization transient fields get default value if we don't serialize them manually.


defaultReadObject() and defaultWriteObject()

This method writes the non-static and non-transient fields of the current class to this stream. If we customize our serialization we can skip this method. We can write and read only the fields we want.

Inheritance in Serialization

  • If super class is serializable then all its subclasses are serializable. Serializing child class object will serialize fields declared in parent class as well.
  • When parent class is not serializable fields declared in parent class does not get serialized. Default constructor of Parent is called after normal deserialization of serializable child class and all the fields declared in parent class get their default value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class Car extends Vehicle implements Serializable {

 private static final long serialVersionUID = -1343317066281169418L;
 
 public Car() {
  System.out.println("Car constuctor is called");
 }
 
 private String model;

 public String getModel() {
  return model;
 }

 public void setModel(String model) {
  this.model = model;
 }
}


public class Vehicle {
 
 public Vehicle() {
  System.out.println("Vehicle constructor is called");
 }
 
 private String name;
 
 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}


public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  Car car = new Car();
  car.setName("Parent");
  car.setModel("Child");
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("car.ser"));
  out.writeObject(car);
  out.close();
  System.out.println("Serialization complete");
 }
}


public class Deserialize {
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("car.ser"));
  Car car = (Car) in.readObject();
  in.close();
  System.out.println(car.getName());
  System.out.println(car.getModel());
 }
}

O/P:- 
Vehicle constructor is called
null
Child


  • If default con is not available in Parent class. Then what happens during deserialization. Booom. java.io.InvalidClassException: no valid constructor
  • If we want the fields defined in non-serializable parent to be serialized then we have to do it manually using readObject() and writeObject().
Q)What if parent is Serializable but you don't want child class to be Serializable.
Ans) Override readObject() and writeObject() and throw exception.

  • Static fields does not get serialized. Because they are not part of Object ad only state of Object is saved during serialization.
readResolve()

A readResolve method to allow a class to designate a replacement object for the object just read from the stream

writeReplace()

A writeReplace method to allow a class to nominate a replacement object to be written to the stream

Enum serialization

  • By default all Enums are Serializable.
  • Enum constants are serialized differently than ordinary serializable or externalizable objects. Only enum name is serialized during enum constant serialization.
  • During serialization only name is serialized and during deserialization the enum is get by calling valueOf() passing name to it. So during deserialization the state present at client side(where deserialization is happening) is returned

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class Student implements Serializable {
 
 private static final long serialVersionUID = -4075933093152302700L;
 
 private String name;
 private Department dept;
 
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public Department getDept() {
  return dept;
 }
 public void setDept(Department dept) {
  this.dept = dept;
 }
}


public enum Department {
 
 CSE(new Address("3rtfloor")), ECE(new Address("4thFloor"));
 Address address;
 
 Department(Address address) {
  this.address = address;
 }

 public Address getAddress() {
  return address;
 }
}

class Address {
 public Address(String address) {
  this.address = address;
 }
 private String address;
 public String getAddress() {
  return address;
 }
}


public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  Student student = new Student();
  student.setName("enum");
  student.setDept(Department.CSE);
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student_enum.ser"));
  out.writeObject(student);
  out.close();
  System.out.println("Serialization complete");
 }
}

O/P:-
Serialization complete


public class Deserialize {
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("student_enum.ser"));
  Student student = (Student) in.readObject();
  in.close();
  System.out.println(student.getDept().getAddress().getAddress());
 }
}

O/P:-

3rtfloor

As you can see in the above case Address is not Serializable. If enum serialization had tried to serialize Address then serialization would have failed.
  • Enum serialization cannot be customized by writeObject(), readObject(), readObjectNoData(), writeReplace(), and readResolve() methods. These are ignored during serialization and desrialization.
  • All enum types have a fixed serialVersionUID of 0L. serialVersionUID are also ignored.
Another example where clients state is returned



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class Student implements Serializable {
 
 private static final long serialVersionUID = -4075933093152302700L;
 
 private String name;
 private Department dept;
 
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public Department getDept() {
  return dept;
 }
 public void setDept(Department dept) {
  this.dept = dept;
 }
}


public enum Department {
 
 CSE(new Address("3rtfloor")), ECE(new Address("4thFloor"));
 Address address;
 
 Department(Address address) {
  this.address = address;
 }

 public Address getAddress() {
  return address;
 }
}

class Address {
 public Address(String address) {
  this.address = address;
 }
 private String address;
 public String getAddress() {
  return address;
 }
}


public class Serialize {
 
 public static void main(String[] args) throws FileNotFoundException, IOException {
  Student student = new Student();
  student.setName("enum");
  Department.CSE.address.setAddress("Address changed");
  student.setDept(Department.CSE);
  ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student_enum.ser"));
  out.writeObject(student);
  out.close();
  System.out.println("Serialization complete");
 }
}

O/P:-
Serialization complete


public class Deserialize {
 public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
  Department.CSE.address.setAddress("Address at client");
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("student_enum.ser"));
  Student student = (Student) in.readObject();
  in.close();
  System.out.println(student.getDept().getAddress().getAddress());
 }
}

O/P:-
Address at client


Inner class serialization

This is what documentation says:-

"Serialization of inner classes (i.e., nested classes that are not static member classes), including local and anonymous classes, is strongly discouraged for several reasons. Because inner classes declared in non-static contexts contain implicit non-transient references to enclosing class instances, serializing such an inner class instance will result in serialization of its associated outer class instance as well. "

Synthetic fields generated by javac (or other JavaTM compilers) to implement inner classes are implementation dependent and may vary between compilers; differences in such fields can disrupt compatibility as well as result in conflicting default serialVersionUID values. The names assigned to local and anonymous inner classes are also implementation dependent and may differ between compilers. 
Since inner classes cannot declare static members other than compile-time constant fields, they cannot use the serialPersistentFields mechanism to designate serializable fields. Finally, because inner classes associated with outer instances do not have zero-argument constructors (constructors of such inner classes implicitly accept the enclosing instance as a prepended parameter), they cannot implement Externalizable. None of the issues listed above, however, apply to static member classes.