This process can also be implemented through the network, which can first create an object on the Windows machine, serialize it, and then send it to a Unix machine over the network, and then re-assemble it accurately. Why can each of RMI, Socket, JMS, and EJB pass Java objects? Of course, it is all due to the object serialization mechanism.
The Java object serialization mechanism generally has two uses:
Java JavaBeans: Bean status information is usually configured at design time. Bean status information must be stored so that the program can restore these status information when it is running. This requires the object's status to be saved to the file and then it can be passed. Read the object state to reconstruct the object and restore the program state.
RMI allows objects on remote machines to be operated like on native machines; or programs that use sockets to transfer objects on the network, these require implementation of serializaiton mechanism.
We can serialize classes by making classes implement the Java.io.Serializable interface. This interface is a manufacturer interface. That is, for the class to implement it, the interface does not need to implement any method. It is mainly used to notify Java virtual machines (JVMs) that an object needs to be serialized.
There are a few points we need to clarify for this:
Not all classes can be serialized. Under cmd, we enter serialver Java.net.Socket to get information about whether the socket is serializable. In fact, the socket is not serializable.
There are many basic classes in Java that have implemented serializable interfaces, such as string, vector, etc. But for example, hashtable does not implement the serializable interface.
There are two main classes for reading or writing objects to streams: ObjectOutputStream and ObjectInputStream.ObjectOutputStream provides a writeObject method used to write objects to the output stream, and ObjectInputStream provides a readObject method for reading objects from the input stream. Objects using these methods must have been serialized. That is, the Serializable interface must have been implemented. If you want to writeobject a hashtable object, then you will get an exception.
The process of serialization is to write objects to and read objects from the byte stream. After converting the object state into a byte stream, you can save it to a file using various byte stream classes in the Java.io package, pipe into another thread, or send object data to another host over a network connection. The object serialization function is very simple and powerful, and it is used in RMI, Socket, JMS, and EJB. The object serialization problem is not the most exciting topic in network programming, but it is quite important and has many practical significance.
Object serialization can implement distributed objects. Main applications such as: RMI uses object serialization to run services on remote hosts, just like when running objects on local machines.
Java object serialization not only retains the data of one object, but also recursively saves the data of each object referenced by the object. The entire object hierarchy can be written into a byte stream, saved in a file, or passed on a network connection. The object serialization can be used to perform "deep copying" of the object, that is, copying the object itself and the referenced object itself. Serializing an object can result in the entire sequence of objects.
Java serialization is relatively simple and usually does not require writing customized code to save and restore object state. Class objects that implement the Java.io.Serializable interface can be converted into or recovered from byte streams without adding any code to the class. Only in rare cases are custom code required to save or restore object state. Note here: Not every class can be serialized, and some classes cannot be serialized. For example, classes involving threads have very complex relationships with specific JVMs.
Serialization mechanism:
Serialization is divided into two parts: serialization and deserialization. Serialization is the first part of this process, breaking the data into a stream of bytes for storage in a file or transmission over a network. Deserialization is to open the byte stream and reconstruct the object. Object serialization not only requires converting the basic data type into byte representation, but sometimes also recovering the data. Recovering data requires an instance of the object that restores data. The serialization process in ObjectOutputStream is connected to a byte stream, including object type and version information. During deserialization, the JVM generates an object instance with header information, and then copies the data from the object byte stream to the object data members. Let’s explain it in two parts:
Processing object flow: ( serialization process and deserialization process)
The Java.io package has two classes that serialize objects. ObjectOutputStream is responsible for writing objects to a byte stream, and ObjectInputStream reconstructs objects from a byte stream.
Let's first understand the ObjectOutputStream class. The ObjectOutputStream class extends the DataOutput interface.
The writeObject() method is the most important method for object serialization. If the object contains references to other objects, the writeObject() method recursively serializes these objects. Each ObjectOutputStream maintains a serialized object reference table to prevent multiple copies of the same object from being sent. (This is important) Since writeObject() can serialize the entire set of cross-referenced objects, the same ObjectOutputStream instance may accidentally be requested to serialize the same object. At this time, antireference serialization is performed instead of writing to the object byte stream again.
Next, let's understand the ObjectOutputStream class from the example.
The code copy is as follows:
// Serialize today's date into a file.
FileOutputStream f = new FileOutputStream ("tmp" );
ObjectOutputStream s = new ObjectOutputStream (f);
s.writeObject("Today" );
s.writeObject(new Date ());
s.flush();
Now, let's understand the ObjectInputStream class. It's similar to ObjectOutputStream. It extends the DataInput interface. Methods in ObjectInputStream mirrors the public method of reading Java basic data types in DataInputStream. The readObject() method deserializes an object from a byte stream. Each time the readObject() method is called, the next Object in the stream is returned. The object byte stream does not transmit the bytecode of the class, but includes the class name and its signature. When readObject() receives the object, the JVM loads the specified class in the header. If this class cannot be found, readObject() throws a ClassNotFoundException. If the object data and bytecode are required, you can use the RMI framework. The rest of the ObjectInputStream methods are used to customize the deserialization process.
Examples are as follows:
The code copy is as follows:
//Deserialize string objects and date objects from the file
FileInputStream in = new FileInputStream ("tmp" );
ObjectInputStream s = new ObjectInputStream (in);
String today = (String )s.readObject();
Date date = (Date )s.readObject();
Customized serialization process:
Serialization can usually be done automatically, but sometimes the process may be controlled. Java can declare classes as serializable, but can still manually control data members declared as static or transient.
Example: A very simple serialization class.
The code copy is as follows:
public class simpleSerializableClass implements Serializable {
String sToday="Today:" ;
transient Date dtToday=new Date ();
}
When serializing, all data members of the class should be serializable except those declared as transient or static. Declaring the variable as transient tells the JVM that we will be responsible for serializing the arguments. After declaring a data member as transient, the serialization process cannot add it to the object byte stream, and there is no data sent from the transient data member. When deserializing the data afterward, the data member needs to be reconstructed (because it is part of the class definition), but does not contain any data because this data member does not write any data to the stream. Remember that object streams do not serialize static or transient. Our class needs to use writeObject() and readObject() methods to process these data members. When using writeObject() and readObject() methods, you should also pay attention to reading these data members in the order they are written.
Some of the codes on how to use custom serialization are as follows
The code copy is as follows:
//Rewrite the writeObject() method to handle the transient members.
public void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.defaultWriteObject();//Make the customized writeObject() method to
//Use the built-in logic in automatic serialization.
outputStream.writeObject(oSocket.getInetAddress());
outputStream.writeInt(oSocket.getPort());
}
//Rewrite the readObject() method to receive transient members.
private void readObject(ObjectInputStream inputStream) throws IOException,
ClassNotFoundException {
inputStream.defaultReadObject();//defaultReadObject() supplements automatic serialization
InetAddress oAddress=(InetAddress )inputStream.readObject();
int iPort =inputStream.readInt();
oSocket = new Socket (oAddress,iPort);
iID=getID();
dtToday =new Date ();
}
Completely customize the serialization process:
If a class is completely responsible for its own serialization, it implements the Externalizable interface instead of the Serializable interface. The Externalizable interface definition includes two methods writeExternal() and readExternal(). These methods can be used to control how object data members are written to byte streams. When the class implements Externalizable, the header is written to the object stream, and then the class is completely responsible for serializing and restoring data members. Apart from the header, there is no automatic serialization at all. Pay attention here. Declaration class implements Externalizable interfaces with significant security risks. The writeExternal() and readExternal() methods are declared public, and malicious classes can use these methods to read and write object data. If the object contains sensitive information, be extra careful. This includes using secure sockets or encrypting the entire byte stream. At this point, we have learned the basic knowledge of serialization.