JaVa
   

Object Serialization

Up until now, we have been looking at quite a low-level way of dealing with writing to and reading from files. In this section you will learn how it is possible to write and read objects to files using serialization, which makes saving and retrieving data far simpler for objects, so we don't need to concern ourselves with saving its data members individually. Serialization is a method of storing all the instance data (see the note at the end of the chapter about the transient keyword to see how to avoid saving certain data) of a class in a single string automatically that can then be sent (for example, via a network or saved to a file). When the data is then read, it is deserialized and returned to its original object form. The best way to see how this works is to look at a simple example. First we need to create a class to describe the object for storing a player's details. So let's make a class called PlayerData, which will store the player's name, score, and username and password for accessing a game. This class can be seen below:

Code Listing 6-5: The PlayerData class
import java.io.*;
public class PlayerData implements Serializable
{
 public PlayerData(String name, int score, String username, String password)
 {
 this.name = name;
 this.score = score;
 this.username = username;
 this.password = password;
 }
 public String name;
 public int score;
 public String username;
 public String password;
}


Java End example

Let's look at how we have made this class serializable. First we need to import the java.io.* package, as it contains the Serializable interface, which we need to make this class implement. This can be seen here:

public class PlayerData implements Serializable


Note that if the class does not implement the Serializable interface, it is not possible to use serialization. The great news is that we do not actually have to do anything else to make serialization work; all we need to do is ensure that we abide by the following three rules:

  1. The Serializable interface must be implemented.

  2. The class must be declared as public.
  3. If the class extends another class, the class it is extending must contain a default constructor (a constructor with no parameters), and the super class must also handle saving its own objects to the stream if data from the super class is to be serialized also. (The super class would need to implement Serializable; otherwise its data would not be recorded but the subclass data would be.)

Now that we have our basic player class, let's look at the main code listing, which will save an instance of this class to a file called output.txt and load it back into a new instance of the class and display what was read into the console. Here is the complete code listing for this example:

Code Listing 6-6: Object serialization
import java.io.*;
public class SerializationExample
{
 public SerializationExample()
 { // Create an instance of our PlayerData class...
 PlayerData playerData = new PlayerData("John", 400, "jsmith", "qwerty");
 // Create out file object...
 File theFile = new File("output.txt");
 // Create the file output stream...
 FileOutputStream fileOutputStream = null;
 try
 {
 fileOutputStream = new FileOutputStream(theFile);
 } catch(FileNotFoundException e)
 {
 System.out.println(e);
 }
 // Create the object output stream...
 ObjectOutputStream objectOutputStream = null;
 try
 {
 objectOutputStream = new ObjectOutputStream
 (fileOutputStream);
 // Write the object to the object output stream...
 objectOutputStream.writeObject(playerData);
 }
 catch(IOException e)
 {
 System.out.println(e);
 }
 // Read the object back into a new instance of our
 // 'PlayerData' class
 PlayerData newPlayerData = null;
 // Create the file input stream...
 FileInputStream fileInputStream = null;
 try
 {
 fileInputStream = new FileInputStream(theFile);
 }
 catch(FileNotFoundException e)
 {
 System.out.println(e);
 }
 // Create the object input stream...
 ObjectInputStream objectInputStream = null;
 try
 {
 objectInputStream = new ObjectInputStream(fileInputStream);
 // Read the object from the object input stream...
 newPlayerData = (PlayerData) objectInputStream
 .readObject();
 }
 catch(ClassNotFoundException e)
 {
 System.out.println(e);
 }
 catch(IOException e)
 {
 System.out.println(e);
 }
 // Print what was read in...
 System.out.println("The player's name was:
 "+newPlayerData.name);
 System.out.println("The player's score was:
 "+newPlayerData.score);
 System.out.println("The player's username was:
 "+newPlayerData.username);
 System.out.println("The player's password was:
 "+newPlayerData.password);
 }
 public static void main(String args[])
 {
 SerializationExample mainApp = new SerializationExample();
 }
}


Java End example

When we execute this console app, we will see that the following is visible in the console window:

Java Click To expand
Screenshot-5: Using serialization

As you can see from this figure, the app has successfully written the data to the file and then read the data back from the file into the app. Let's now look at the main code that we have used to create this. First, we create a PlayerData object called playerData with the following line of code.

PlayerData playerData = new PlayerData("John", 400, "jsmith", "qwerty");


We pass the details into the constructor, which are the name, score, username, and password. Now that we have our object, we next create a File object so we can specify where we wish our output (and input) file to be in this example. This is accomplished with the following line of code:

File theFile = new File("output.txt");


Once we have our File object, we then need to create a FileOuputStream. This is done by passing our theFile object into the FileOutputStream constructor, as can be seen in the following block of code:

FileOutputStream fileOutputStream = null;
try
{
 fileOutputStream = new FileOutputStream(theFile);
}
catch(FileNotFoundException e)
{
 System.out.println(e);
}


Note that we need to catch the FileNotFoundException. Once we have our fileOutputStream object, we then use this to create an ObjectOutputStream, which is used to write objects to a stream (in this case, our FileOutputStream). So to create our ObjectOutputStream, we pass our fileOutputStream into the constructor, as can be seen in the following block of code:

// Create the object output stream...
 ObjectOutputStream objectOutputStream = null;
 try
 {
 objectOutputStream = new ObjectOutputStream
 (fileOutputStream);


Once our objectOutputStream object is created, we can then easily write our playerData object to the file with the following line of code:

objectOutputStream.writeObject(playerData);


Notice how we use the writeObject method, which is a member of the objectOutputStream class and takes any serializable object as a parameter. Now that the data is stored in the file, create a new PlayerData object called newPlayerData and set it to null. This is done with the following line of code:

PlayerData newPlayerData = null;


Next, we need to create a FileInputStream that we can use to load the file back in. We create the FileInputStream using the same theFile object as we did with the FileOutputStream. We will be accessing the same file that we just wrote. This is done with the following lines of code:

FileInputStream fileInputStream = null;
try
{
 fileInputStream = new FileInputStream(theFile);
} catch(FileNotFoundException e)
{
 System.out.println(e);
}


Again, notice that we need to catch the FileNotFoundException exception. Once we have our fileInputStream object, we then need to create an ObjectInputStream, which can be used to read our PlayerData object back in from the file. Here is the code we require to create our ObjectInputStream.

ObjectInputStream objectInputStream = null;
try
{
 objectInputStream = new ObjectInputStream(fileInputStream);


Notice how we use our fileInputStream object in the constructor to create the ObjectInputStream so it will use the file as the stream when attempting to read the data. We now need to read the object back in, which is done by using the readObject method of the objectInputStream object. Note that we also need to typecast the data that we read in to the correct class type (which in our example is PlayerData). This can be seen in the following line of code:

newPlayerData = (PlayerData) objectInputStream.readObject();


The final part of the reading is to catch both a ClassNotFoundException exception and an IOException exception. We need to catch a ClassNotFoundException as a security measure, as we cannot be assured of the type of object being read in, at least Java cannot be assured of us writing correct code for this. Now that we have read in our player information, all that is left is to simply display it to the console to show that the data has been read back in correctly.

NoteĀ 

If the class you wish to serialize contains a member that you do not actually wish to save to a file (or send across a network), you can declare the variable as transient. For example, if we had a password stored as a string in a class and we did not wish to serialize it, we would declare it as follows:

transient private String password;
JaVa
   
Comments