- There must be a default constructor for the class.
- There must be accessors and mutators for all the instance variables of the class. Actually this is overstating the requirement but is a good base rule: read the Hibernate documentation for the full details.
- The class should implement
Serializable. Strictly speaking, this is not a requirement. However, in practice you will normally want your Hibernate objects to be serializable so that they can be (potentially) migrated around a multiprocessor cluster or saved and restored across a web server reboot etc. - The class should have an
idinstance variable, usually of typeLong. Again this is not a true requirement but it is recommended to use automatically generated surrogate keys and, if so, to use an instance variable calledidto hold it. Certainly, alternatives are possible. - The mutator for the
idproperty should be private, not public. Again not a requirement but good practice. You should never update theidproperty directly but rather rely on Hibernate updating it for you. In practice, it is the value of this field that Hibernate uses to decide if an object has been mapped to a database record or not. Change the property yourself and you could seriously confuse Hibernate. - You should decide on a business key for the object and implement the
equalsandhashCodemethods for it. - You should add any extra type specific constructors (which should leave the
idfieldnull) and business rule methods you like. - You should not make the class
finalif you want to be able to use lazy loading for objects of the class.
Tuesday, 5 July 2011
Requirements for object to become Hibernate Object
Monday, 9 May 2011
Sunday, 17 April 2011
Collections and serializablity
Set doesn't extends Serializable interface but its implementations (HashSet, TreeSet, EnumSet) implements Serializable interface and so contains methods writeObject and readObject to save or reconstitute the state of Set (HashSet, TreeSet, EnumSet) instance to/from a stream. When you serialize this instance, writeObject and readObject methods will be called.
Collections like Vector and Hashtable both implements Serializable and have been designed for serialization.
One thing to keep in mind is to watch out for is, that in order to serialize a collection like Vector or Hashtable, you must also be able to serialize all of the objects contained in these collections. Otherwise, the collection would not be able to be completely restored. Your program will throw a NotSerializableException unless all objects stored in the Vector or Hashtable are also serializable.
Eg.
If your class implements Serializable and all of its members are serializable, then the object can be serialized correctly. Example:
public class Person implements Serializable {
private String name;
private Collection<Integer> medals = new ArrayList<Integer>();
}
How is set implemented in java?
But the question is how set maintains unique elements? Here is how. Internally set maintains a Map when a Set (HashSet/Treeset/etc) is instantiated. See below code of HashSet constructor.
/**
* Constructs a new, empty set; the backing HashMap instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap();
}
So HashSet creates an instance of HashMap when HashSet is instantiated, TreeSet creates an instance of TreeMap when TreeSet is instantiated and so on. The keys in the map are the elements you add to Set. Then what are values? Values are dummy objects. When you add an element to the Set, the element will be added as key and "new Object()" (dummy Object instance) will be added as value which is a dummay value. What will happen if you add a duplicate object to Set? If the set already contains the element, the call to add method leaves the set unchanged and returns false. If the set does not contain the element, the call to add method adds the element and returns true.
Externalization vs serialization
Final words on serialization and externalization:
In earlier version of Java, reflection was very slow, and so serializaing large object graphs (e.g. in client-server RMI applications) was a bit of a performance problem. To handle this situation, the java.io.Externalizable interface was provided, which is like java.io.Serilizable but with custom-written mechanisms to perform the marshalling and unmarshalling fuctions (you need to implement readExternal and writeExternal methods on your class). This gives you the means to get around the reflection performance bottleneck.
In recent versions of java (1.3 onwards, certainly) the performance of reflection is vastly better than it used to be, and so this is much less of a problem.
Also, the built-in Java serialization mechanism isn't the only one, you can get third-party replacements, such as JBoss Serialization, which is considerably quicker, and is a drop-in replacement for the default.
A big downside of Externalizable is that you have to maintain this logic yourself - if you add, remove or change a field in your class, you have to change your writeExternal/readExternal methods to account for it, as mentioned in post on limitations of serialization.
In summary, Externalizable is of the Java 1.1 days. There's really no need for it any more. Only
when you need it very much, than only use it.
References : http://download.oracle.com/javase/tutorial/javabeans/persistence/index.html
Difference in sizes when you serialize using Serializable interface and serialize using Externalizable interface
Length: 220
Magic: ACED
Version: 5
OBJECT
CLASSDESC
Class Name: "SimpleClass"
Class UID: -D56EDC726B866EBL
Class Desc Flags: SERIALIZABLE;
Field Count: 4
Field type: object
Field name: "firstName"
Class name: "Ljava/lang/String;"
Field type: object
Field name: "lastName"
Class name: "Ljava/lang/String;"
Field type: float
Field name: "weight"
Field type: object
Field name: "location"
Class name: "Ljava/awt/Point;"
Annotation: ENDBLOCKDATA
Superclass description: NULL
STRING: "Brad"
STRING: "Pitt"
float: 180.5
OBJECT
CLASSDESC
Class Name: "java.awt.Point"
Class UID: -654B758DCB8137DAL
Class Desc Flags: SERIALIZABLE;
Field Count: 2
Field type: integer
Field name: "x"
Field type: integer
Field name: "y"
Annotation: ENDBLOCKDATA
Superclass description: NULL
integer: 49.345
integer: 67.567
Now if you serialize the same by extending Externalizable interface, the size will be reduced drastically and the information saved in the persistant store is also reduced a lot. Here is the result of serializing the same class, modified to be externalizable. Notice that the actual data is not parseable externally any more--only your class knows the meaning of the data!
Length: 54
Magic: ACED
Version: 5
OBJECT
CLASSDESC
Class Name: "SimpleClass"
Class UID: 5CB3777417A3AB5BL
Class Desc Flags: EXTERNALIZABLE;
Field Count: 0
Annotation
ENDBLOCKDATA
Superclass description
NULL
EXTERNALIZABLE:
[70 00 04 4D 61 72 6B 00 05 44 61 76 69 73 43 3C
80 00 00 00 00 01 00 00 00 01]
Limitation of serialization
Performance issues:
- Serialization is a recursive algorithm. What I mean to say here is, apart from the fields that are required, starting from a single object, until all the objects that can be reached from that object by following instance variables, are also serialized. This includes the super class of the object until it reaches the "Object" class and the same way the super class of the instance variables until it reaches the "Object" class of those variables. Basically all the objects that it can read. This leads to lot of overheads. Say for example, you need only car type and licence number but using serialization, you cannot stop there. All the information that includes description of car, its parts, blah blah will be serialized. Obviously this slows down the performance.
- Both serializing and deserializing require the serialization mechanism to discover information about the instance it is serializing. Using the default serialization mechanism, will use reflection to discover all the field values. Also the information about class description is added to the stream which includes the descption of all the serializable superclasses, the description of the class and the instance data associated with the specific instance of the class. Lots of data and metadata and again performance issue.
- You know that serialization needs serialVersionUID, a unique Id to identify the information persisted. If you dont explicitly set a serialiVersionUID, serialization will compute the serialiVersionUID by going through all the fields and methods. So based on the size of the class, again serialization mechanism takes respective amount of time to calculate the value. A third performance issue.
Other issues
Above three points confirm serialization has performance issues. Apart from performance issues,
When an object that implements Serializable interface, is serialized or de-serialized, no constructor of the object is called and hence any initialization which is done in the constructor cannot be done. Although there is an alternative of writing all initialization logic in a separate method and call it in constructor and readObject methods so that when an object is created or deserialized, the initialization process can happen but it definitely is a messy approach.
Limitation of externalization
Externalization on the other hand isn't very flexible and requires you to rewrite your marshalling and demarshalling code whenever you change your class definitions.
As you know a default public no-arg constructor will be called when serializing the objects that implements Externalizable interface. Hence, Externalizable interface can't be implemented by Inner Classes in Java as all the constructors of an inner class in Java will always accept the instance of the enclosing class as a prepended parameter and therefore you can't have a no-arg constructor for an inner class. Inner classes can achieve object serialization by only implementing Serializable interface.
If you are subclassing your externalizable class, you have to invoke your superclass’s implementation. So this causes overhead while you subclass your externalizable class. Observe the examples above where the superclass writeExternal method is explicitly called in the subclass writeExternal method.
Methods in externalizable interface are public. So any malicious program can invoke which results into loosing the prior serialized state.
Once your class is tagged with either Serializable or Externalizable, you can't change any evolved version of your class to the other format. You alone are responsible for maintaining compatibility across versions. That means that if you want the flexibility to add fields in the future, you'd better have your own mechanism so that you can skip over additional information possibly added by those future versions.
Using transient keyword in serialization
import java.io.*;
import java.util.*;
class LoginCredentials implements Serializable {
private String username;
private transient String password;
LoginCredentials(String name, String password) {
username = name;
this.password = password;
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
LoginCredentials = new LoginCredentials("peter","mikhalenko");
}
}
Externalization : The Externalizable interface
You can control the process of serialization by implementing the Externalizable interface instead of Serializable. This interface extends the original Serializable interface and adds writeExternal() and readExternal(). So its not a marker interface. These two methods will automatically be called in your object's serialization and deserialization, allowing you to control the whole process.
There is one major difference between serialization and externalization: When you serialize an Externalizable object, a default constructor will be called automatically; only after that will the readExternal() method be called.
How serialization happens?
JVM first checks for the Externalizable interface and if object supports Externalizable interface, then serializes the object using writeExternal method. If the object does not support Externalizable but implement Serializable, then the object is saved using ObjectOutputStream. Now when an Externalizable object is reconstructed, an instance is created first using the public no-arg constructor, then the readExternal method is called. Again if the object does not support Externalizable, then Serializable objects are restored by reading them from an ObjectInputStream.
Following Listing shows how you can use externalization.
import java.io.*;
import java.util.*;
class Data implements Externalizable {
inti;
String s;
public Data() {
System.out.println("Data default constructor");
}
public Data(String x, int a) {
System.out.println("Second constructor");
s = x; i = a;
}
public String toString() {
return s + i;
}
public void writeExternal(ObjectOutput out)
throws IOException {
out.writeObject(s);
out.writeInt(i);
}
public void readExternal(ObjectInput in) {
s = (String)in.readObject();
i = in.readInt();
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
Data d = new Data("String value",1514);
System.out.println(d);
ObjectOutputStream o = new ObjectOutputStream(
New FileOutputStream("data.out"));
o.writeObject(d);
o.close();
// Now deserialize
ObjectInputStream in = new ObjectInputStream(
new FileInputStream("data.out"));
d = (Data)in.readObject();
}
}
If you inherit some class from a class implementing the Externalizable interface, you must call writeExternal() and readExternal() methods when you serialize or deserialize this class in order to correctly save and restore the object.
What is a java marker interface?
Java Marker Interface Examples:
java.lang.Cloneable
java.io.Serializable
java.util.EventListener
Lets take the java.io.Serializable marker interface. It doesnot has any members defined it it. When a java class is to be serialized, you should intimate the java compiler in some way that there is a possibility of serializing this java class. In this scenario, marker interfaces are used. The java class which may be serialized has to implement the java.io.Serializable marker interface. In such way, we are intimating the java compiler.
From java 1.5, the need for marker interface is eliminated by the introduction of the java annotation feature. So, it is wise to use java annotations than the marker interface. It has more feature and advantages than the java marker interface.
Tuesday, 12 April 2011
Serialization of enums
Serialization mechanism ensures that new enum instances are not created on serialization and de-serialization (ensuring singleton behaviour). In a distributed environment when an enum is serialized from one JVM to another JVM, enum's state is not serialized. Hence, it is not encouraged to have state with enums which is updated by the applications.
You can only have constant attributes that are initialized by the constructor.
Eg.
import java.io.Serializable;
public class Status {
public enum Status implements Serializable{
STATUS_OPEN(0, "open"),
STATUS_STARTED(1, "started"),
STATUS_INPROGRESS(2, "inprogress"),
STATUS_ONHOLD(3, "onhold"),
STATUS_COMPLETED(4, "completed"),
STATUS_CLOSED(5, "closed");
private final int status;
private final String description;
Status(int aStatus, String desc){
this.status = aStatus;
this.description = desc;
}
public int status(){
return this.status;
}
public String description(){
return this.description;
}
}
}
Sunday, 13 March 2011
Example on Format of a Serialized Object
Example Code
The following code illustrates the writing and reading of Serializable.ObjectReaderWriter is the primary application class. At runtime it displays a "Save As..." FileDialog, allowing you to specify an output file to receive the stream containing the serialized objects. (All the sample objects are written to the same file.) It then prompts for an input file from which to read a stream.
This arrangement of the sample code allows you to write out the serialized data to one file, make changes to the class format for one or more of the data classes, recompile and rerun, and attempt to read one of the older versions back in.
The class MySerialObject contains a reference to an instance of the class MyInternalObject, to demonstrate the saving of nested object references in the stream. MySerialObject also contains a field (of type int) that is marked transient, and upon restoration you will find that the default value 0 gets assigned to that variable.
The class MyVersionObject demonstrates the use of versioning with a programmer-specified SUID. You only need to change the SUID when you make changes to the class structure that render it incompatible with older versions of that same class, and whose serialized instances have previously been written to disk.
You can compile the .java (source) files using the javac (Java compiler) tool included in the MRJ SDK Tools folder, or using the Java compiler in CodeWarrior or Visual Cafe. You can then optionally create a .jar (Java archive) file containing the resulting .class (output) files.
The archive for this article includes the .java and .class files, and a .jar file containing the .class files. To run the program, drag either the file ObjectReaderWriter.class or ObjectReaderWriter.jar onto the JBindery application icon, which is located in the MRJ SDK JBindery folder. Once JBindery launches, it will display ObjectReaderWriter in the "class name" field. (This field specifies the name of the class to run at application startup; that class must contain a main() method.) Click OK to run the program.
ObjectReaderWriter.java The class that will read and write serialized and externalized objects.
import java.awt.*;
import java.io.*;
public class ObjectReaderWriter {
String filePath;
public static void main( String args[] ) {
ObjectReaderWriter orw = new ObjectReaderWriter();
}
ObjectReaderWriter() {
try {
// Create instances of each data class to be serialized.
MySerialObject serialObject = new MySerialObject();
MyExternObject externObject = new MyExternObject();
MyVersionObject versionObject = new MyVersionObject();
// Allow the user to specify an output file.
FileDialog fd = new FileDialog( new Frame(),
"Save As...", FileDialog.SAVE );
fd.show();
filePath = new String( fd.getDirectory() + fd.getFile() );
// Create a stream for writing.
FileOutputStream fos = new FileOutputStream( filePath );
// Next, create an object that can write to that file.
ObjectOutputStream outStream =
new ObjectOutputStream( fos );
// Save each object.
outStream.writeObject( serialObject );
externObject.writeExternal( outStream );
outStream.writeObject( versionObject );
// Finally, we call the flush() method for our object, which
forces the data to
// get written to the stream:
outStream.flush();
// Allow the user to specify an input file.
fd = new FileDialog( new Frame(), "Open...",
FileDialog.LOAD );
fd.show();
filePath = new String( fd.getDirectory() + fd.getFile() );
// Create a stream for reading.
FileInputStream fis = new FileInputStream( filePath );
// Next, create an object that can read from that file.
ObjectInputStream inStream = new ObjectInputStream( fis );
// Retrieve the Serializable object.
serialObject = ( MySerialObject )inStream.readObject();
// Display what we retrieved:
System.out.println( serialObject.getS() );
System.out.println( "i = " + serialObject.getI() );
serialObject.displayInternalObjectAttrs();
// Retrieve the Externalizable object.
externObject.readExternal( inStream );
// Display what we retrieved:
System.out.println( externObject.getS() );
System.out.println( "i = " + externObject.getI() );
// Retrieve the versioned object.
versionObject = ( MyVersionObject )
inStream.readObject();
// Display what we retrieved:
System.out.println( versionObject.getS() );
System.out.println( "i = " + versionObject.getI() );
// Display the SUID of the versioned class in the VM,
// not necessarily the serialized object.
ObjectStreamClass myObject = ObjectStreamClass.lookup(
Class.forName( "MyVersionObject" ) );
long theSUID = myObject.getSerialVersionUID();
System.out.println
( "The SUID of class MyVersionObject = " + theSUID );
}
catch ( InvalidClassException e ) {
System.out.println( "InvalidClassException..." );
}
catch ( ClassNotFoundException e ) {
System.out.println( "ClassNotFoundException..." );
}
catch ( OptionalDataException e ) {
System.out.println( "OptionalDataException..." );
}
catch ( FileNotFoundException e ) {
System.out.println( "FileNotFoundException..." );
}
catch ( IOException e ) {
System.out.println( "IOException..." );
}
}
}
Listing 2: MyInternalObject.java
MyInternalObject.java The nested data class.
import java.io.*;
public class MyInternalObject implements Serializable {
private int i;
private String s;
MyInternalObject() {
i = 128;
s = new String( "Instance of MyInternalObject..." );
}
public int getI() {
return i;
}
public String getS() {
return s;
}
}
Listing 3: MyVersionObject.java
MyVersionObject.java The versioned data class.
import java.io.*;
public class MyVersionObject implements Serializable
{
static final long serialVersionUID = 1L;
private int i; private String s;
// Uncomment the next two lines to verify that default values will
// be substituted if
// the value is not present in the stream at deserialization time.
// private int i2 = -1;
// private String s2 = "This is the new String field";
MyVersionObject() {
i = 512;
s = new String( "Instance of MyVersionObject..." );
}
public int getI() {
return i;
}
public String getS() {
return s;
}
}
transient keyword Example
Externalized class example
Versioning in Serialization
The stream reading the serialized representation is responsible for accounting for any differences. The intent is that a newer version of a Java class should be able to interoperate with older representations of the same class, as long as there have not been certain changes in the class structure. The same does not necessarily hold true for an older version of the class, which may not be able to effectively deal with a newer representation.
So, we need some way to determine at runtime (or more appropriately, deserialization-time) whether we have the necessary backward compatibility.
In Java 1.1, changes to classes may be specified using a version number. A specific class variable, serialVersionUID (representing the Stream Unique Identifier, or SUID), may be used to specify the earliest version of the class that can be deserialized. The SUID is declared as follows:
static final long serialVersionUID = 2L;
This particular declaration and assignment specifies that version 2 is as far back as this class can go. It is not compatible with an object written by version 1 of the class, and it cannot write a version 1 object. If it encounters a version 1 object in a stream (such as when restoring from a file), an InvalidClassException will be thrown.
The SUID is a measure of backward compatibility. The same SUID can be used for multiple representations of a class, as long as newer versions can still read the older versions.
If you do not explicitly assign a SUID, a default value will be assigned when the object gets serialized. This default SUID is a hash, or unique numeric value, which is computed using the class name, interfaces, methods, and fields. The exact algorithm is defined by the Secure Hash Algorithm (SHA). Refer to the Sun Java documentation for details.
The JDK (MRJ) utility program serialver will display the default (hash) SUID for a class. You can then paste this value in any subsequent, compatible versions of the class. (It is not required in the initial version of the class.) As of this writing the serialver program has not been included in the MRJ SDK, but hopefully will be in the future.
How can you obtain the SUID for a class at runtime to determine compatibility? First, query the Virtual Machine for information about the class represented in the stream, using methods of the class ObjectStreamClass. Here is how we can get the SUID of the current version of the class named MyClass, as known to the Virtual Machine:
ObjectStreamClass myObject = ObjectStreamClass.lookup(
Class.forName( "MyClass" ) );
long theSUID = myObject.getSerialVersionUID();
Now when we restore an Externalizable object, we can compare its SUID to the class SUID just obtained. If there is a mismatch, we should take appropriate action. This may involve telling the user that we cannot handle the restoration, or we may have to assign and use some default values.
If we are restoring a Serializable object, the runtime will check the SUID for us when it attempts to read values from the stream. If you override readObject(), you will want to compare the SUIDs there.
How do you determine what changes between class versions are acceptable? For an earlier version, which may contain fewer fields, trying to read a serialized object from a later version of the same class may cause problems. There is a tendency to add fields to a class as that class evolves, which means that the earlier version does not know about the newer fields. In contrast, since a newer version of a class may look for fields that are not present in the older version, it assigns default values to those fields.
This can be seen in the example code when we add a new field to the MyVersionObject class, but don't update the SUID. The new class can still read the older stream representation, even though no values exist in that stream for the new fields. It assigns 0 to the new int, and null to the new String, but doesn't throw any exceptions. If we then increment the SUID (from 1 to 2) to indicate that we do not consider older class versions compatible with this version, we throw an InvalidClassException when attempting to read a version 1 object from the stream.
The Sun documentation lists the various class format changes that can adversely affect the restoration of an object. A few of these include:
- Deleting a field, or changing it from non-static or non-transient to static or transient, respectively.
- Changing the position of classes in a hierarchy.
- Changing the data type of a primitive field.
- Changing the interface for a class from Serializable to Externalizable (or vice-versa).
- Adding fields, which will result in default values (based on data type) being assigned to the new fields upon restoration.
- Adding classes will still allow an object of the added class to be created, since the class structure information is included in the stream. However, its fields will be set to the default values.
- Adding or removing the writeObject() or readObject() methods.
- Changing the access modifier (public, private, etc.) for a field, since it is still possible to assign a value to the field.
- Changing a field from static or transient to to non-static or non-transient, respectively.
Java Serialization in gist
What is Serialization?
Simply put serialization is a method to deflate an object to store it on the system.
So how do you do it?
Simply implement the Serializable interface. Once you implement that interface, not only does your base class deflate, even you child classes deflate! What do I mean by all this, well read on.
Implementing Serialization
- Implement the interface serialzable.
Code:
class Example implements Serializable.
The above step tells the complier that you are going to implement serialization.
Which basically means that the object is going to be saved. Time to write the object to the disk.
This simply creates an output stream, pipes it up with an object output stream and writes the deflated object to disk. The name of the file is SerializedFile.txt.FileInputStream fIn = new FileInputStream("SerializedFile.txt");
ObjectInputStream inSt = new ObjectInputStream(fIn);
b3 = (ObjectType)inSt.readObject();Then you decide to bring the objects back to life. So you follow the reverse process, which, naturally is called deserialization!
FileInputStream fIn = new FileInputStream("SerializedFile.txt");
ObjectInputStream inSt = new ObjectInputStream(fIn);
b3 = (ObjectType)inSt.readObject();What if you have something top secret, like your password which should not be saved. You mark it as transient!
My guess is that you understood nothing till now. And rightfully so, its too much to take in one go. So here is a detailed theoretical explanation.
- Serializaation is used to save an object and all the objects that inherit from the one serialized. They too are saved.
- The objects whose references are in the serialized object is also saved!
- The reason all these objects have to be saved is because when the main object is brought back to life, the references it points to must not be null. They should point to some valid objects.
- That can only happen if all the objects referenced are saved with the base object.
- In case you want that something should not be saved, you simply prefix it with the word transient.
- When a serialized object is brought back to life, the transient variables are initialized to null.
- Static variables, functions and classes cannot be serialized.
- One very peculiar thing to note is that the constructors are not called again when the object is deserialized. This will be made amply clear by the example given below.
The Code
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Base implements Serializable{
Base()
{
System.out.println("In Base Constructor");
}
public void function()
{
System.out.println("In a function");
}
}
class Derived extends Base{
Derived()
{
System.out.println("In Derived Constructor");
}
}
public class SerializeObj {
public static void main(String[] args) {
Base b1 = new Base();
Base b2 = new Base();
//---------------Writing to disk------------------------
try{
b1.function();
b2.function();
FileOutputStream fOut = new FileOutputStream("SerializedFile.txt");
ObjectOutputStream outSt = new ObjectOutputStream(fOut);
outSt.writeObject(b1);
outSt.writeObject(b2);
outSt.close();
}
catch(Exception e){
e.printStackTrace();
}
System.out.println("End of serialization");
System.out.println("Begin Deserialization");
//---------------Reading from disk----------------------
try{
FileInputStream fIn = new FileInputStream("SerializedFile.txt");
ObjectInputStream inSt = new ObjectInputStream(fIn);
Base b3 = (Base)inSt.readObject();//note object created
Base b4 = (Base)inSt.readObject();
b3.function();
b4.function();
inSt.close();
}
catch(Exception e){
e.printStackTrace();
}
}
}
Output
In Base Constructor
In Base Constructor
In a function
In a function
End of serialization
In a function
In a function
Thursday, 24 February 2011
Java Serialization : Introduction
Introduction
Serialization is the process of saving an object in a storage medium (such as a file, or a memory buffer) or to transmit it over a network connection in binary form. The serialized objects are JVM independent and can be re-serialized by any JVM. In this case the "in memory" java objects state are converted into a byte stream. This type of the file can not be understood by the user. It is a special types of object i.e. reused by the JVM (Java Virtual Machine). This process of serializing an object is also called deflating or marshalling an object. Note : Note that its ‘primary purpose’ is to write an object into a stream, so that it can be transported through a network and that object can be rebuilt again, but sometimes people use java serialization as a replacement for database, which is not our main motive. Just a placeholder where you can persist an object across sessions. This is not the primary purpose of java serialization. Sometimes, when I interview candidates for Java I hear them saying java serialization is used for storing (to preserve the state) an object and retrieving it. They use it synonymously with database. This is a wrong perception for serialization. Other ways you can leverage the feature of serialization is, you can use it to perform a deep copy.
You can see this tutorial for quick tutorial.
Overview
Serialization is the process of saving an object in a storage medium (such as a file, or a memory buffer) or to transmit it over a network connection in binary form. The serialized objects are JVM independent and can be re-serialized by any JVM. In this case the "in memory" java objects state are converted into a byte stream. This type of the file can not be understood by the user. It is a special types of object i.e. reused by the JVM (Java Virtual Machine). This process of serializing an object is also called deflating or marshalling an object. But when deserializing the object, the deserializer must know by some protocol how to deserialize. Here protocol means, understanding between serializing person and de-serializing person.Implementation
For an object to be serialized, it must be an instance of a class that implements either the Serializable or Externalizable interface in java.io. Serializable is type of marker interface. Both interfaces only permit the saving of data associated with an object's variables. They depend on the class definition being available to the Java Virtual Machine at reconstruction time in order to construct the object.The Serializable interface relies on the Java runtime default mechanism to save an object's state. Writing an object is done via the writeObject() method in the ObjectOutputStream class (or the ObjectOutput interface). Writing a primitive value may be done through the appropriate write<datatype>() method.
Reading the serialized object is accomplished using the readObject() method of the ObjectInputStream class, and primitives may be read using the various read<datatype>() methods.
What about other objects that may be referred to by the object we are serializing? For instance, what if our object is a Frame containing a set of (AWT) Panel and TextArea instance variables? Using the Serializable interface, these references (and their associated data) also are converted and written to the stream. All state information necessary to reconstruct our Frame object and any objects that it references gets stored together.
If those other objects or their formats weren't stored, our reconstructed Frame would contain null object references, and the content of those Panels and TextAreas would be gone. Plus, any methods that rely on the existence of the Panels or TextAreas would throw exceptions.
The Externalizable interface specifies that the implementing class will handle the serialization on its own, instead of relying on the default runtime mechanism. This includes which fields get written (and read), and in what order. The class must define a writeExternal() method to write out the stream, and a corresponding readExternal() method to read the stream. Inside of these methods the class calls ObjectOutputStream writeObject(), ObjectInputStream readObject(), and any necessary write<datatype>() and read<datatype>() methods, for the desired fields.
Hiding Data From Serialization
Sometimes you may wish to prevent certain fields from being stored in the serialized object. The Serializable interface allows the implementing class to specify that some of its fields do not get saved or restored. This is accomplished by placing the keyword transient before the data type in the variable declaration. For example, you may have some data which is confidential and can be re-read from a master file later (as opposed to saving it with the serialized object). Or you decide (wisely) to preserve the privacy of file references by declaring any such variables as transient. Otherwise, all fields automatically get written without any additional effort by the class.In addition to those fields declared as transient, static fields are not serialized (written out), and so cannot be deserialized (read back in).
Another way to use Serializable, and control which fields get written, is to override the writeObject() method of the Serializable interface. Inside of this method, you are responsible for writing out the appropriate fields. If you take this approach, you will want to override readObject() as well, to control the restoration process. This is similar to using Externalizable, except that interface requires writeExternal() and readExternal().
For the Externalizable interface, since both writeExternal() and readExternal() must be declared public, this increases the risk that a rogue object could use them to determine the format of the serialized object. For this reason, you should be careful when saving object data with this interface.
It is worth considering the amount of security you need for any objects that you serialize. When reading them back in, all of the normal Java security checks (such as the bytecode verifier) are in effect. You can define certain values within the class that should remain intact in serialized objects. Perhaps they should contain a specific value, or a value within a particular range. You can easily check the value of any numeric variable read in from a serialized object, especially if you know that only a portion of the available range for that data type is used by your variable.
You can also encrypt the outgoing data stream. The implementation is up to you, and don't forget to decrypt the object format when reading it back in.
Versioning
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class SerializationBox implements Serializable {
private byte serializableProp = 10;
public byte getSerializableProp() {
return serializableProp;
}
}
public class SerializationSample {
public static void main(String args[]) throws IOException,
FileNotFoundException, ClassNotFoundException {
SerializationBox serialB = new SerializationBox();
serialize("serial.out", serialB);
SerializationBox sb = (SerializationBox) deSerialize("serial.out");
System.out.println(sb.getSerializableProp());
}
public static void serialize(String outFile, Object serializableObject)
throws IOException {
FileOutputStream fos = new FileOutputStream(outFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(serializableObject);
}
public static Object deSerialize(String serilizedObject)
throws FileNotFoundException, IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream(serilizedObject);
ObjectInputStream ois = new ObjectInputStream(fis);
return ois.readObject();
}
}
Conclusion
Adding object persistence to Java applications using serialization is easy. Serialization allows you to save the current state of an object to a container, typically a file. At some later time, you can retrieve the saved data values and create an equivalent object. Depending on which interface you implement, you can choose to have the object and all its referenced objects saved and restored automatically, or you can specify which fields should be saved and restored. Java also provides several ways of protecting sensitive data in a serialized object, so objects loaded from a serialized representation should prove no less secure than those classes loaded at application startup. Versioning provides a measure of the backward compatibility of class versions. The code needed to add serialization to your application is simple and flexible.