Serialization is best described by its absence. In Section the section called “The TCP Client/Server”, we passed String
objects from
the client to the server and back again and we were not concerned with the transport system.
That is a correct approach, however, things get a little bit more difficult when we want to send objects
that consist of a number of states (i.e. is-a-part-of values). Before the advent of serialization we (as the programmer)
were required to break an object into its component parts and send them one-by-one over the stream, where they would
then be re-constructed one-by-one. So for example, if an object Fish
contained a name and a type
String
states, these would have to be written one at a time to the stream using a method
like writeUTF()
. The server would then have to read these String
objects
one at a time and re-construct the Fish
object. However, this is prone to
error; Suppose the order of string reading was reversed on the server, then the fish would have the name of type and the
type of name.
Object serialization makes this task straightforward, as we simply add to the code the fact that the
Fish
class implements Serializable
and almost everything else is
taken care of.
The serialization mechanism used is capable of handling a wide variety of situations. When you serialize an object
you save all the states of the object. This even includes states marked private
. However, there
are times when you don't want a state to be persistent, especially if the object is tied to resources that are specific
to this session of the VM. It does not make any sense to serialize the object for later use. Fortunately, the
Java programming language includes the declaration transient
. A state marked transient means that
the the state is not saved when an object is serialized. Static data is also not serializable, and so is ignored.
If the private
states in your class include sensitive data, e.g. credit card numbers, passwords etc.
you should not serialize them as this data can be plainly read in the stream. The simplest way to remove this data is to
use private transient
.
import java.io.*; // contains the Serializable interface public class Fish implements Serializable { private String name; private String type; public Fish(String name, String type) { this.name = name; this.type = type; } public String getName() { return this.name; } public String getType() { return this.type; } }
The Fish
class implements the Serializable
interface, which
allows the data of the class to be ordered in a serial form, to send it across a serial network (of sorts). The
Serializable
interface is simple to implement, as we
do not have to write any additional code. The Serializable
interface does not have any methods,
as it is simply a "signal" interface that indicates to the JVM that you want to use the default serialization mechanism.
On the client-side we can create a fish object (say myFish) and send it
using the send(Object o)
method discussed previously. In the HandleConnection
class we can read in each object, check to see what class it is, and then cast convert the object to a Fish.
So, for example:
if (o.getClass().getName().equals("Fish")) { Fish aFish = (Fish) o; }
Once we have converted to the Fish
object we can simply call the usual methods:
System.out.println("\nFish name is: "+ aFish.getName()); System.out.println("\nFish type is: "+ aFish.getType());
Once an object is written to a stream, its state is fixed. In effect a copy of the object is created on the client side and sent to the server side. If this object changes state on the client side, while the server is busy processing the copy, then the copy is no longer up-to-date. This can be problematic. One solution is to lock the client object on the client side, so that it cannot be altered while the copied object is in transit to or from the server - but what happens if the server or communication fails?
The use of serialization also allows easy persistant storage through the use of files. For example if we wished
to store the Fish
objects to a file we could use:
FileOutputStream out = new FileOutputStream("Storage.file"); ObjectOutput s = new ObjectOutputStream(out); s.writeObject("Some example string"); // store an exampleString
s.writeObject(new Fish("Jim", "Guppy")); // store an exampleFish
s.flush();
That allows us to store a String
object or a Fish
object using the same
writeObject()
method. To read in from this file Storage.file
we could use:
FileInputStream in = new FileInputStream("Storage.file"); ObjectInputStream s = new ObjectInputStream(in); String theString = (String) s.readObject(); Fish myFish = (Fish) s.readObject();
As the communication becomes more complex, the serialization task becomes even more difficult. Serialization, although providing
a huge advantage over manually streaming data, still requires the programmer to maintain the protocol for
communication, distributing the protocols and making sure that both the client and server are aware of the
serialized object class (in this case Fish
), allowing the casting to take place.
Task: Modify the code from the section called “The TCP Client/Server” to send a Fish
object across the
network and display it on the server side. Use these code samples (from the section called “The TCP Client/Server”):
© 2006
Dr. Derek Molloy
(DCU).