Difference between revisions of "Serialization"
From Suhrid.net Wiki
Jump to navigationJump to search (Created page with "== Intro == * Mechanism to persist state of objects * ObjectOutputStream.writeObject() - serialize and write * ObjectInputStream.readObject() - read and deserialize [[Catego...") |
|||
(5 intermediate revisions by the same user not shown) | |||
Line 4: | Line 4: | ||
* ObjectOutputStream.writeObject() - serialize and write | * ObjectOutputStream.writeObject() - serialize and write | ||
* ObjectInputStream.readObject() - read and deserialize | * ObjectInputStream.readObject() - read and deserialize | ||
+ | * Object and its complete object graph being seralized must implement the Serializable interface. | ||
+ | * If any object needs to be skipped from the serialization process - mark it as transient. | ||
+ | * In below example, Thing is not serializable, so when a Thing is used as a field in Majig the field is marked as transient. | ||
+ | <syntaxhighlight lang="java5"> | ||
+ | class Thing { | ||
+ | |||
+ | private String name; | ||
+ | |||
+ | public Thing(String name) { | ||
+ | this.name = name; | ||
+ | } | ||
+ | |||
+ | public void setName(String name) { | ||
+ | this.name = name; | ||
+ | } | ||
+ | |||
+ | public String getName() { | ||
+ | return name; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | class Majig implements Serializable { | ||
+ | private int id; | ||
+ | |||
+ | private transient Thing t; | ||
+ | |||
+ | public int getID() { | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | Majig(int id, Thing t) { | ||
+ | this.id = id; | ||
+ | this.t = t; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public class Ser1 { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | |||
+ | Majig m1 = new Majig(1, new Thing("T1")); | ||
+ | Majig m2 = new Majig(2, new Thing("T2")); | ||
+ | |||
+ | File savFile = new File("majig.sav"); | ||
+ | |||
+ | try { | ||
+ | ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(savFile)); | ||
+ | |||
+ | os.writeObject(m1); | ||
+ | os.writeObject(m2); | ||
+ | |||
+ | System.out.println("Serialized m1 and m2"); | ||
+ | |||
+ | os.close(); | ||
+ | } catch (IOException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | == writeObject and readObject== | ||
+ | |||
+ | * When serialized objects are deserialized, what happens to the transient fields ? They will be null. | ||
+ | * writeObject and readObject are callback methods that we can define in the code which offer a way to save some part of the object's state manually. | ||
+ | * This is the contract which must be followed: The methods must be defined in the class which we are trying to serialize. | ||
+ | |||
+ | <syntaxhighlight lang="java5"> | ||
+ | |||
+ | private void writeObject(ObjectOutputStream os) { | ||
+ | } | ||
+ | |||
+ | private void readObject(ObjectInputStream is) { | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | * For e.g. in the above case since Thing is transient, we can save Thing's name field instead during serialization and rebuild Thing object using the name during deserialization. | ||
+ | * Before we save/retrieve our custom field, os.defaultWriteObject() and defaultReadObject() must be called to handle the normal serialization process. | ||
+ | |||
+ | <syntaxhighlight lang="java5"> | ||
+ | |||
+ | import java.io.*; | ||
+ | |||
+ | class Thing { | ||
+ | |||
+ | private String name; | ||
+ | |||
+ | public Thing(String name) { | ||
+ | this.name = name; | ||
+ | } | ||
+ | |||
+ | public void setName(String name) { | ||
+ | this.name = name; | ||
+ | } | ||
+ | |||
+ | public String getName() { | ||
+ | return name; | ||
+ | } | ||
+ | |||
+ | public String toString() { | ||
+ | return getName(); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | class Majig implements Serializable { | ||
+ | private int id; | ||
+ | |||
+ | private transient Thing t; | ||
+ | |||
+ | public int getID() { | ||
+ | return id; | ||
+ | } | ||
+ | |||
+ | public Thing getThing() { | ||
+ | return t; | ||
+ | } | ||
+ | |||
+ | Majig(int id, Thing t) { | ||
+ | this.id = id; | ||
+ | this.t = t; | ||
+ | } | ||
+ | |||
+ | private void writeObject(ObjectOutputStream os) { | ||
+ | try { | ||
+ | os.defaultWriteObject(); | ||
+ | os.writeUTF(t.getName()); | ||
+ | } catch (IOException ioe) { | ||
+ | ioe.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | private void readObject(ObjectInputStream is) { | ||
+ | try { | ||
+ | is.defaultReadObject(); | ||
+ | t = new Thing(is.readUTF()); | ||
+ | } catch (Exception ioe) { | ||
+ | ioe.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | public class Ser1 { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | |||
+ | Majig m1 = new Majig(1, new Thing("T1")); | ||
+ | Majig m2 = new Majig(2, new Thing("T2")); | ||
+ | |||
+ | File savFile = new File("majig.sav"); | ||
+ | |||
+ | try { | ||
+ | ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(savFile)); | ||
+ | |||
+ | os.writeObject(m1); | ||
+ | os.writeObject(m2); | ||
+ | |||
+ | System.out.println("Serialized m1 and m2"); | ||
+ | |||
+ | os.close(); | ||
+ | |||
+ | ObjectInputStream is = new ObjectInputStream(new FileInputStream(savFile)); | ||
+ | |||
+ | Majig newm1 = (Majig) is.readObject(); | ||
+ | Majig newm2 = (Majig) is.readObject(); | ||
+ | |||
+ | System.out.println("Deserialized m1 and m2"); | ||
+ | |||
+ | System.out.println(newm1.getID() + ", " + newm1.getThing()); | ||
+ | System.out.println(newm2.getID() + ", " + newm2.getThing()); | ||
+ | |||
+ | is.close(); | ||
+ | |||
+ | } catch (Exception e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | == Inheritance and Serialization == | ||
+ | |||
+ | * During deser, constructor does not run and instance initializers are not run. | ||
+ | * However every non-serializable superclass's constructor will run ! | ||
+ | * So a non-serializable superclass constructor can overrwrite an instance variable's value if it runs! | ||
+ | |||
+ | <syntaxhighlight lang="java5"> | ||
+ | |||
+ | import java.io.*; | ||
+ | |||
+ | class Gadget { | ||
+ | |||
+ | int val = 10; | ||
+ | |||
+ | protected int getVal() { | ||
+ | return val; | ||
+ | } | ||
+ | |||
+ | protected void setVal(int val) { | ||
+ | this.val = val; | ||
+ | } | ||
+ | |||
+ | Gadget() { | ||
+ | System.out.println("Gadget Constructor"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Phone extends Gadget implements Serializable { | ||
+ | |||
+ | Phone() { | ||
+ | System.out.println("Phone Constructor"); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | public class Ser2 { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | |||
+ | try { | ||
+ | |||
+ | File savFile = new File("phone.sav"); | ||
+ | ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(savFile)); | ||
+ | Phone op = new Phone(); | ||
+ | op.setVal(30); | ||
+ | os.writeObject(op); | ||
+ | System.out.println("Serialized Phone with val :" + op.getVal()); | ||
+ | os.close(); | ||
+ | |||
+ | ObjectInputStream is = new ObjectInputStream(new FileInputStream(savFile)); | ||
+ | Phone p = (Phone) is.readObject(); | ||
+ | System.out.println("Deserialized Phone"); | ||
+ | System.out.println("Phone Val : " + p.getVal()); | ||
+ | is.close(); | ||
+ | |||
+ | } catch (Exception e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | /* | ||
+ | |||
+ | Output : Note Phone Constructor does not run during deser, but Gadget does thus overrwriting val | ||
+ | |||
+ | Gadget Constructor | ||
+ | Phone Constructor | ||
+ | Serialized Phone with val :30 | ||
+ | Gadget Constructor | ||
+ | Deserialized Phone | ||
+ | Phone Val : 10 | ||
+ | |||
+ | */ | ||
+ | |||
+ | |||
+ | </syntaxhighlight> | ||
[[Category:OCPJP]] | [[Category:OCPJP]] |
Latest revision as of 01:00, 30 August 2011
Intro
- Mechanism to persist state of objects
- ObjectOutputStream.writeObject() - serialize and write
- ObjectInputStream.readObject() - read and deserialize
- Object and its complete object graph being seralized must implement the Serializable interface.
- If any object needs to be skipped from the serialization process - mark it as transient.
- In below example, Thing is not serializable, so when a Thing is used as a field in Majig the field is marked as transient.
class Thing {
private String name;
public Thing(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Majig implements Serializable {
private int id;
private transient Thing t;
public int getID() {
return id;
}
Majig(int id, Thing t) {
this.id = id;
this.t = t;
}
}
public class Ser1 {
public static void main(String[] args) {
Majig m1 = new Majig(1, new Thing("T1"));
Majig m2 = new Majig(2, new Thing("T2"));
File savFile = new File("majig.sav");
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(savFile));
os.writeObject(m1);
os.writeObject(m2);
System.out.println("Serialized m1 and m2");
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
writeObject and readObject
- When serialized objects are deserialized, what happens to the transient fields ? They will be null.
- writeObject and readObject are callback methods that we can define in the code which offer a way to save some part of the object's state manually.
- This is the contract which must be followed: The methods must be defined in the class which we are trying to serialize.
private void writeObject(ObjectOutputStream os) {
}
private void readObject(ObjectInputStream is) {
}
- For e.g. in the above case since Thing is transient, we can save Thing's name field instead during serialization and rebuild Thing object using the name during deserialization.
- Before we save/retrieve our custom field, os.defaultWriteObject() and defaultReadObject() must be called to handle the normal serialization process.
import java.io.*;
class Thing {
private String name;
public Thing(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return getName();
}
}
class Majig implements Serializable {
private int id;
private transient Thing t;
public int getID() {
return id;
}
public Thing getThing() {
return t;
}
Majig(int id, Thing t) {
this.id = id;
this.t = t;
}
private void writeObject(ObjectOutputStream os) {
try {
os.defaultWriteObject();
os.writeUTF(t.getName());
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
private void readObject(ObjectInputStream is) {
try {
is.defaultReadObject();
t = new Thing(is.readUTF());
} catch (Exception ioe) {
ioe.printStackTrace();
}
}
}
public class Ser1 {
public static void main(String[] args) {
Majig m1 = new Majig(1, new Thing("T1"));
Majig m2 = new Majig(2, new Thing("T2"));
File savFile = new File("majig.sav");
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(savFile));
os.writeObject(m1);
os.writeObject(m2);
System.out.println("Serialized m1 and m2");
os.close();
ObjectInputStream is = new ObjectInputStream(new FileInputStream(savFile));
Majig newm1 = (Majig) is.readObject();
Majig newm2 = (Majig) is.readObject();
System.out.println("Deserialized m1 and m2");
System.out.println(newm1.getID() + ", " + newm1.getThing());
System.out.println(newm2.getID() + ", " + newm2.getThing());
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Inheritance and Serialization
- During deser, constructor does not run and instance initializers are not run.
- However every non-serializable superclass's constructor will run !
- So a non-serializable superclass constructor can overrwrite an instance variable's value if it runs!
import java.io.*;
class Gadget {
int val = 10;
protected int getVal() {
return val;
}
protected void setVal(int val) {
this.val = val;
}
Gadget() {
System.out.println("Gadget Constructor");
}
}
class Phone extends Gadget implements Serializable {
Phone() {
System.out.println("Phone Constructor");
}
}
public class Ser2 {
public static void main(String[] args) {
try {
File savFile = new File("phone.sav");
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(savFile));
Phone op = new Phone();
op.setVal(30);
os.writeObject(op);
System.out.println("Serialized Phone with val :" + op.getVal());
os.close();
ObjectInputStream is = new ObjectInputStream(new FileInputStream(savFile));
Phone p = (Phone) is.readObject();
System.out.println("Deserialized Phone");
System.out.println("Phone Val : " + p.getVal());
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
Output : Note Phone Constructor does not run during deser, but Gadget does thus overrwriting val
Gadget Constructor
Phone Constructor
Serialized Phone with val :30
Gadget Constructor
Deserialized Phone
Phone Val : 10
*/