In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-22 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article focuses on "Java serialization and deserialization how to achieve", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let the editor take you to learn how to serialize and deserialize Java.
The concept of serialization and deserialization
Serialization is the process of transforming the state information of an object into a form that can be stored or transmitted. It is common to store an object to a storage medium, such as a file or a cache. In the process of network transmission, it can be in the format of bytes or XML. The byte or XML encoding format can restore identical objects. This opposite process is also known as deserialization.
Serialization and deserialization of Java objects
In Java, we can create an object in a variety of ways, and we can reuse it as long as it is not recycled. However, the Java objects we created exist in JVM's heap memory.
These objects can only exist when JVM is running. Once JVM stops running, the state of these objects is lost.
But in a real application scenario, we need to persist these objects and be able to re-read them when needed. Java's object serialization can help us achieve this functionality.
Object serialization mechanism (object serialization) is a built-in method of object persistence in Java language. Through object serialization, the state of an object can be saved as a byte array, and the byte array can be converted into an object by deserialization when necessary.
Object serialization can easily convert between active objects and byte arrays (streams) in JVM.
In Java, object serialization and deserialization are widely used in RMI (remote method invocation) and network transmission.
Related interfaces and classes
Java provides a set of convenient API support for developers to serialize and deserialize Java objects. These include the following interfaces and classes:
Java.io.Serializablejava.io.ExternalizableObjectOutputObjectInputObjectOutputStreamObjectInputStreamSerializable interface
Class enables its serialization capabilities by implementing the java.io.Serializable interface.
Classes that do not implement this interface will not be able to serialize or deserialize any of their states. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and is only used to identify serializable semantics. There are no methods and fields in this interface, so why can only objects of classes that implement the interface be serialized?
When trying to serialize an object, you encounter an object that does not support the Serializable interface. In this case, a NotSerializableException will be thrown.
If the class to be serialized has a parent class, if you want to persist variables defined in the parent class at the same time, the parent class should also integrate the java.io.Serializable interface.
Here is a class that implements the java.io.Serializable interface
Public class serialization and deserialization {public static void main (String [] args) {} / / Note that the inner class cannot be serialized because it depends on the outer class @ Test public void test () throws IOException {AA = new A (); A.I = 1; a.s = "a"; FileOutputStream fileOutputStream = null; FileInputStream fileInputStream = null Try {/ / write obj to file fileOutputStream = new FileOutputStream ("temp"); ObjectOutputStream objectOutputStream = new ObjectOutputStream (fileOutputStream); objectOutputStream.writeObject (a); fileOutputStream.close (); / / read obj fileInputStream = new FileInputStream ("temp") through file; ObjectInputStream objectInputStream = new ObjectInputStream (fileInputStream) Aa2 = (A) objectInputStream.readObject (); fileInputStream.close (); System.out.println (a2.i); System.out.println (a2.s); / / print the result as before serialization} catch (IOException e) {e.printStackTrace ();} catch (ClassNotFoundException e) {e.printStackTrace () } class An implements Serializable {int i; String s;}
Externalizable interface
In addition to Serializable, another serialization interface, Externalizable, is provided in java
To understand the difference between the Externalizable interface and the Serializable interface, let's first look at the code. Let's change the above code to use Externalizable.
Class B implements Externalizable {/ / must have a public no-parameter constructor. Otherwise, public B () {} int i; String s; @ Override public void writeExternal (ObjectOutput out) throws IOException {} @ Override public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {}} @ Test public void test2 () throws IOException, ClassNotFoundException {BB = new B (); b.I = 1; b.s = "a" / / write obj to file FileOutputStream fileOutputStream = new FileOutputStream ("temp"); ObjectOutputStream objectOutputStream = new ObjectOutputStream (fileOutputStream); objectOutputStream.writeObject (b); fileOutputStream.close (); / / read obj FileInputStream fileInputStream = new FileInputStream ("temp") through file; ObjectInputStream objectInputStream = new ObjectInputStream (fileInputStream); B2 = (B) objectInputStream.readObject (); fileInputStream.close () System.out.println (b2.i); System.out.println (b2.s); / / the print result is 0 and null, that is, the initial value, not assigned / / 0 / / null}
From the above example, we can find that the values of all the properties of the object after serialization and deserialization of class B become the default values. That is, the state of the previous object has not been persisted. This is the difference between the Externalizable interface and the Serializable interface:
Externalizable inherits Serializable, which defines two abstract methods: writeExternal () and readExternal ().
Developers need to override the writeExternal () and readExternal () methods when using the Externalizable interface for serialization and deserialization. Because the serialization implementation details are not defined in these two methods in the above code, the output is empty.
It is also worth noting that when serializing with Externalizable, when the object is read, the no-parameter constructor of the serialized class is called to create a new object, and then the values of the saved object's fields are populated into the new object. Therefore, the class that implements the Externalizable interface must provide a no-parameter constructor for public.
Class C implements Externalizable {int i; int j; String s; public C () {} / / implements the following two methods to select the members to be copied in the serialization. / / and the write order and read order must be the same, otherwise an error will be reported. / / multiple variables of the same type can be written in the same order. Override public void writeExternal (ObjectOutput out) throws IOException {out.writeInt (I); out.writeInt (j); out.writeObject (s);} @ Override public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException {I = in.readInt (); j = in.readInt (); s = (String) in.readObject () } @ Test public void test3 () throws IOException, ClassNotFoundException {C c = new C (); c. I = 1; c. J = 2; c. S = "a"; / / write obj to file FileOutputStream fileOutputStream = new FileOutputStream ("temp"); ObjectOutputStream objectOutputStream = new ObjectOutputStream (fileOutputStream); objectOutputStream.writeObject (c); fileOutputStream.close () / / read obj FileInputStream fileInputStream = new FileInputStream ("temp"); ObjectInputStream objectInputStream = new ObjectInputStream (fileInputStream); C c2 = (C) objectInputStream.readObject (); fileInputStream.close (); System.out.println (c2.i); System.out.println (c2.j); System.out.println (c2.s) / / the print result is 0 and null, that is, the initial value, which is not assigned / / 0 / / null} serialize ID
Serialize ID problem situation: two clients An and B try to transfer object data through the network. A side serializes object C into binary data and then transmits it to BMagne B to deserialize C.
Problem: the full classpath of C object is assumed to be com.inout.Test, and there is a class file on both An and B sides, and the functional code is exactly the same. The Serializable interface is also implemented, but deserialization always indicates that it is not successful.
Solution: whether the virtual machine allows deserialization depends not only on whether the classpath and function code are consistent, but also on whether the serialization ID of the two classes is consistent (that is, private static final long serialVersionUID = 1L). In listing 1, although the functional code of the two classes is exactly the same, the serialization ID is different, and they cannot serialize and deserialize each other.
Package com.inout; import java.io.Serializable; public class An implements Serializable {private static final long serialVersionUID = 1L; private String name; public String getName () {return name;} public void setName (String name) {this.name = name;}} package com.inout; import java.io.Serializable; public class An implements Serializable {private static final long serialVersionUID = 2L Private String name; public String getName () {return name;} public void setName (String name) {this.name = name;}} static variables do not participate in serialization
The main method in listing 2 serializes the object, modifies the value of the static variable, reads the serialized object, and then gets the value of the static variable through the read object and prints it out. According to listing 2, does this System.out.println (t.staticVar) statement output 10 or 5?
Public class Test implements Serializable {private static final long serialVersionUID = 1L; public static int staticVar = 5; public static void main (String [] args) {try {/ / initially staticVar is 5 ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream ("result.obj")); out.writeObject (new Test ()); out.close () / / serialized and modified to 10 Test.staticVar = 10; ObjectInputStream oin = new ObjectInputStream (new FileInputStream ("result.obj")); Test t = (Test) oin.readObject (); oin.close () / / read again, print the new value System.out.println (t.staticVar) via t.staticVar;} catch (FileNotFoundException e) {e.printStackTrace ();} catch (IOException e) {e.printStackTrace ();} catch (ClassNotFoundException e) {e.printStackTrace () }}}
The final output is 10, and for incomprehensible readers, the printed staticVar is obtained from the read object and should be the state it was saved in. The reason for printing 10 is that static variables are not saved when serialized, which is easier to understand. Serialization saves the state of the object, and static variables belong to the state of the class, so serialization does not save static variables.
Explore the serialization of ArrayList
ArrayList serialization before introducing ArrayList serialization, let's consider one issue:
How to customize serialization and deserialization policies
With this question in mind, let's take a look at the source code of java.util.ArrayList
Public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable {private static final long serialVersionUID = 8683452581122892189L; transient Object [] elementData; / / non-private to simplify nested class access private int size;}
The author omits the other member variables, and you can see from the above code that ArrayList implements the java.io.Serializable interface, so we can serialize and deserialize it.
Because elementData is transient (1.8 seems to change this), we don't think this member variable will be serialized and preserved. Let's write a Demo to test our idea:
Serialization of public class ArrayList {public static void main (String [] args) throws IOException, ClassNotFoundException {ArrayList list = new ArrayList (); list.add ("a"); list.add ("b"); ObjectOutputStream objectOutputStream = new ObjectOutputStream (new FileOutputStream ("arr")); objectOutputStream.writeObject (list); objectOutputStream.close (); ObjectInputStream objectInputStream = new ObjectInputStream (new FileInputStream ("arr")) ArrayList list1 = (ArrayList) objectInputStream.readObject (); objectInputStream.close (); System.out.println (Arrays.toString (list.toArray (); / / serialized successfully, the elements in it remain unchanged. }
Anyone who knows ArrayList knows that the underlying ArrayList is implemented through arrays. Then the array elementData is actually used to hold the elements in the list. We know from the way this property is declared that it cannot be persisted through serialization. So why does the result of code 4 preserve the elements in List through serialization and deserialization?
WriteObject and readObject methods
The following methods are defined in ArrayList: writeObject and readObject.
Here is the conclusion:
During serialization, if the writeObject and readObject methods are defined in the serialized class, the virtual machine attempts to call the writeObject and readObject methods in the object class for user-defined serialization and deserialization.
If there is no such method, the default calls are the defaultWriteObject method of ObjectOutputStream and the defaultReadObject method of ObjectInputStream.
User-defined writeObject and readObject methods allow users to control the serialization process, such as dynamically changing the serialization value during the serialization process.
Let's take a look at the implementation of these two methods:
Private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {elementData = EMPTY_ELEMENTDATA; / / Read in size, and any hidden stuff s.defaultReadObject (); / / Read in capacity s.readInt (); / / ignored if (size > 0) {/ / be like clone (), allocate array based upon size not capacity ensureCapacityInternal (size) Object [] a = elementData; / / Read in all elements in the proper order. For (int iTuno; iwriteOrdinaryObject--- > writeSerialData--- > invokeWriteObject)
Take a look at invokeWriteObject here:
Void invokeWriteObject (Object obj, ObjectOutputStream out) throws IOException, UnsupportedOperationException {if (writeObjectMethod! = null) {try {writeObjectMethod.invoke (obj, new Object [] {out});} catch (InvocationTargetException ex) {Throwable th = ex.getTargetException (); if (th instanceof IOException) {throw (IOException) th } else {throwMiscException (th);}} catch (IllegalAccessException ex) {/ / should not occur, as access checks have been suppressed throw new InternalError (ex);}} else {throw new UnsupportedOperationException ();}}
Where writeObjectMethod.invoke (obj, new Object [] {out}); is the key, calling the writeObjectMethod method through reflection. This is how the official interpretation of the writeObjectMethod:
Class-defined writeObject method, or null if none
In our example, this method is the writeObject method we defined in ArrayList. It is called by reflection.
At this point, let's try to answer the question just asked:
If a class contains writeObject and readObject methods, how are these two methods called? Answer: when using the writeObject method of ObjectOutputStream and the readObject method of ObjectInputStream, it is called through reflection. Why implement Serializable?
At this point, we have introduced the serialization of ArrayList. So, I don't know if anyone has asked such a question:
Serializable is obviously an empty interface. How does it ensure that only methods that implement this interface can be serialized and deserialized?
Definition of Serializable interface: public interface Serializable {} readers can try to remove the code that inherits Serializable from code 1, and then execute code 2, which will throw a java.io.NotSerializableException.
As a matter of fact, this question is also easy to answer. Let's go back to the call stack of writeObject in ObjectOutputStream:
WriteObject-- > writeObject0-- > writeOrdinaryObject--- > writeSerialData--- > invokeWriteObject
There is a piece of code in the writeObject0 method:
If (obj instanceof String) {writeString ((String) obj, unshared);} else if (cl.isArray ()) {writeArray (obj, desc, unshared);} else if (obj instanceof Enum) {writeEnum ((Enum) obj, desc, unshared);} else if (obj instanceof Serializable) {writeOrdinaryObject (obj, desc, unshared) } else {if (extendedDebugInfo) {throw new NotSerializableException (cl.getName () + "\ n" + debugInfoStack.toString ());} else {throw new NotSerializableException (cl.getName ());}}
When serializing, it determines whether the class to be serialized is of type Enum, Array, and Serializable, and if not, throws a NotSerializableException directly.
Summary of serialized knowledge points
1. If a class wants to be serialized, you need to implement the Serializable interface. Otherwise, a NotSerializableException exception is thrown because the type is checked during the serialization operation, requiring that the serialized class belong to any of the Enum, Array, or Serializable types.
2. Serialize and deserialize objects through ObjectOutputStream and ObjectInputStream
3. Whether the virtual machine allows deserialization depends not only on whether the classpath and function code are consistent, but also on whether the serialization ID of the two classes is consistent (that is, private static final long serialVersionUID).
Serialization ID provides two generation strategies under Eclipse, one is a fixed 1L, the other is to randomly generate a non-repetitive long type data (actually generated using the JDK tool), here is a suggestion, if there is no special requirement, is to use the default 1L, this can ensure that the code is consistent when the deserialization is successful. So what is the use of randomly generated serialized ID? sometimes it can be used to limit the use of certain users by changing the serialized ID.
4. Serialization does not save static variables.
5. If you want to serialize the parent object, you need to have the parent class also implement the Serializable interface.
6. The function of the Transient keyword is to control the serialization of the variable. Adding the keyword before the variable declaration can prevent the variable from being serialized into the file. After being deserialized, the value of the transient variable is set to the initial value, such as the int type is 0 and the object type is null.
7. The server sends serialized object data to the client, and some data in the object is sensitive, such as a password string. It is hoped that the password field will be encrypted when serialized, and if the client has a decryption key, the password can be read only when the client is deserialized, which can ensure the data security of the serialized object to a certain extent.
8. Add writeObject and readObject methods to the class to implement a custom serialization policy
At this point, I believe you have a deeper understanding of "how to achieve Java serialization and deserialization". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.