Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize serialization and deserialization in ava

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

This article will explain in detail how to achieve serialization and deserialization in ava. The content of the article is of high quality, so the editor shares it for you as a reference. I hope you will have a certain understanding of the relevant knowledge after reading this article.

Meaning

Serialization: the object is written to the IO stream, and the implementation object becomes a file. Deserialization: the objects in the file are recovered and restored to memory to deserialize. Meaning: the greatest significance of serialization is that objects can be transferred across hosts, and these objects can be saved on disk and exist independently of the program.

Serialization needs to implement an interface

An object needs to be serialized, and here, you need to implement an interface, which is

Java.io.Serializable

Click on this interface and you can see that the definition is as follows

Public interface Serializable {

}

Serialize

To turn an Java object into an array, you need to use a stream to write a Java object to the byte stream. The code is as follows

Import java.io.*

Import java.util.Arrays

Public class Main {

Public static void main (String [] args) throws IOException {

ByteArrayOutputStream buffer = new ByteArrayOutputStream ()

Try (ObjectOutputStream output = new ObjectOutputStream (buffer)) {

/ / write int:

Output.writeInt (12345)

/ / write String:

Output.writeUTF ("Hello")

/ / write Object:

Output.writeObject (Double.valueOf (123.456))

}

System.out.println (Arrays.toString (buffer.toByteArray ()

}

}

Here, through the ObjectOutputStream type, the data is written into the buffer.

What is written here are the basic data types, which are int,boolean or String, because these basic data types implement a serialized interface, so the content of the write is very large.

Deserialization

ObjectInputStream is responsible for reading objects from a byte stream. The code is as follows

Try (ObjectInputStream input = new ObjectInputStream (...)) {

Int n = input.readInt ()

String s = input.readUTF ()

Double d = (Double) input.readObject ()

}

Here, to avoid the incompatibility of the Java class when it is not serialized. Because there is a class class on one host and no class class on the other, it needs to be deserialized. Also identify the version and use serialVersionUID to define a static version. Examples are as follows

Public class Person implements Serializable {

Private static final long serialVersionUID = 2709425275741743919L

}

Principle

Here we will explain the principle of Java serialization. The principle is a little boring, and the guest can skip it.

A piece of serialization code

Public class SerializeTest {

Public void serialize () throws Exception {

Data1 d = new data1 ()

D.setId (1036)

D.setName ("data1")

D.setPwd ("pwd1")

D.setPwd2 ("pwd2")

FileOutputStream fos = new FileOutputStream ("d:/project/serial/data1")

ObjectOutputStream oos = new ObjectOutputStream (fos); / / create Object output stream object

Oos.writeObject (d); / / write serialized data data1 class to the data1 file

Fos.close ()

Oos.close ()

System.out.println (serialization complete)

}

Public data1 deSerialize () throws Exception {

FileInputStream fis = new FileInputStream ("d:/project/serial/data1")

ObjectInputStream ois = new ObjectInputStream (fis); / / create Object input stream object

Data1 d = (data1) ois.readObject (); / / deserialize data1 class data from the data1 file

Ois.close ()

Fis.close ()

Return d

}

Public static void main (String [] args) throws Exception {

SerializeTest s = new SerializeTest ()

S.serialize ()

Data1 d = s.deSerialize ()

System.out.println ("id:" + d.getId ())

System.out.println ("name:" + d.getName ())

System.out.println ("pwd:" + d.getPwd ())

}

}

After serialization, this is what happens when you open it with notdpad++

View in hexadecimal

That is, these are serialization

Source code parsing

Serialization relies on ObjectOutputStream. Structural parameters

Public ObjectOutputStream (OutputStream out) throws IOException {

VerifySubclass ()

Bout = new BlockDataOutputStream (out)

Handles = new HandleTable (10, (float) 3.00)

Subs = new ReplaceTable (10, (float) 3.00)

EnableOverride = false

WriteStreamHeader ()

Bout.setBlockDataMode (true)

If (extendedDebugInfo) {

DebugInfoStack = new DebugTraceInfoStack ()

} else {

DebugInfoStack = null

}

}

The writeStreamHeader code is as follows

Protected void writeStreamHeader () throws IOException {

Bout.writeShort (STREAM_MAGIC)

Bout.writeShort (STREAM_VERSION)

}

WriteShort writes two bytes to the container, where 4 bytes are initialized (one STREAM_MAGIC, one STREAM_VERSION)

/ * *

* Magic number that is written to the stream header.

, /

Final static short STREAM_MAGIC = (short) 0xaced

/ * *

* Version number that is written to the stream header.

, /

Final static short STREAM_VERSION = 5

That is, ac ed 00 05, which declares the use of the serialization protocol and describes the serialized version

Start serializing writeObject ()

Public final void writeObject (Object obj) throws IOException {

If (enableOverride) {

WriteObjectOverride (obj)

Return

}

Try {

WriteObject0 (obj, false)

} catch (IOException ex) {

If (depth = = 0) {

WriteFatalException (ex)

}

Throw ex

}

}

WriteObject0 () is usually called directly.

Private void writeObject0 (Object obj, boolean unshared)

Throws IOException

{

Boolean oldMode = bout.setBlockDataMode (false)

Depth++

Try {

/ / handle previously written and non-replaceable objects

Int h

... Omit the code

If (obj instanceof ObjectStreamClass) {

WriteClassDesc ((ObjectStreamClass) obj, unshared)

Return

}

/ / check for replacement object

Object orig = obj

Class cl = obj.getClass ()

ObjectStreamClass desc

For (;;) {

/ / REMIND: skip this check for strings/arrays?

Class repCl

Desc = ObjectStreamClass.lookup (cl, true)

If (! desc.hasWriteReplaceMethod ()) | |

(obj = desc.invokeWriteReplace (obj)) = = null | |

(repCl = obj.getClass ()) = = cl)

{

Break

}

Cl = repCl

}

/ / remaining cases

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 ())

}

}

} finally {

Depth--

Bout.setBlockDataMode (oldMode)

}

}

For those later judgments, it is easy to see that serialized data is written in different ways depending on the type of object. Here, if the object implements the Serializable interface, the writeOrdinaryObject () method is called.

Then it turns out that this method also passes in a desc, which is the ObjectStreamClass class created in a for (;;) loop before this function to describe the object class information.

Then watch writeOrdinaryObject ()

Private void writeOrdinaryObject (Object obj

ObjectStreamClass desc

Boolean unshared)

Throws IOException

{

If (extendedDebugInfo) {

DebugInfoStack.push (

(depth = = 1? "root": ") +" object (class "" +)

Obj.getClass () .getName () + "," + obj.toString () + ")

}

Try {

Desc.checkSerialize ()

Bout.writeByte (TC_OBJECT)

WriteClassDesc (desc, false)

Handles.assign (unshared? Null: obj)

If (desc.isExternalizable () & &! desc.isProxy ()) {

WriteExternalData ((Externalizable) obj)

} else {

WriteSerialData (obj, desc)

}

} finally {

If (extendedDebugInfo) {

DebugInfoStack.pop ()

}

}

}

First, writeByte (), write a byte of TC_OBJECT flag bit (hexadecimal 73), and then call writeClassDesc (desc) to write this kind of information generated before, follow up to see writeClassDesc ()

Private void writeClassDesc (ObjectStreamClass desc, boolean unshared)

Throws IOException

{

Int handle

If (desc = = null) {

WriteNull ()

} else if (! unshared & & (handle = handles.lookup (desc))! =-1) {

WriteHandle (handle)

} else if (desc.isProxy ()) {

WriteProxyDesc (desc, unshared)

} else {

WriteNonProxyDesc (desc, unshared)

}

}

IsProxy () determines whether the class is a dynamic proxy class, and has no knowledge of dynamic proxy (first mark). Here, because it is not a dynamic proxy class, it will call the

WriteNonProxyDesc (desc)

Follow up writeNonProxyDesc (desc)

Private void writeNonProxyDesc (ObjectStreamClass desc, boolean unshared)

Throws IOException

{

Bout.writeByte (TC_CLASSDESC)

Handles.assign (unshared? Null: desc)

If (protocol = = PROTOCOL_VERSION_1) {

/ / do not invoke class descriptor write hook with old protocol

Desc.writeNonProxy (this)

} else {

WriteClassDescriptor (desc)

}

Class cl = desc.forClass ()

Bout.setBlockDataMode (true)

If (cl! = null & & isCustomSubclass ()) {

ReflectUtil.checkPackageAccess (cl)

}

AnnotateClass (cl)

Bout.setBlockDataMode (false)

Bout.writeByte (TC_ENDBLOCKDATA)

WriteClassDesc (desc.getSuperDesc (), false)

}

Found that writeByte wrote a byte of TC_CLASSDESC (hexadecimal 72)

Then the next judgment is that true enters writeNonProxy ()

WriteNonProxy ()

Void writeNonProxy (ObjectOutputStream out) throws IOException {

Out.writeUTF (name)

Out.writeLong (getSerialVersionUID ())

Byte flags = 0

If (externalizable) {

Flags | = ObjectStreamConstants.SC_EXTERNALIZABLE

Int protocol = out.getProtocolVersion ()

If (protocol! = ObjectStreamConstants.PROTOCOL_VERSION_1) {

Flags | = ObjectStreamConstants.SC_BLOCK_DATA

}

} else if (serializable) {

Flags | = ObjectStreamConstants.SC_SERIALIZABLE

}

If (hasWriteObjectData) {

Flags | = ObjectStreamConstants.SC_WRITE_METHOD

}

If (isEnum) {

Flags | = ObjectStreamConstants.SC_ENUM

}

Out.writeByte (flags)

Out.writeShort (fields.length)

For (int I = 0; I < fields.length; iTunes +) {

ObjectStreamField f = fields [I]

Out.writeByte (f.getTypeCode ())

Out.writeUTF (f.getName ())

If (! f.isPrimitive ()) {

Out.writeTypeString (f.getTypeString ())

}

}

}

Calling writeUTF () writes the class name, the writeUTF () function, which writes a two-byte class name length before writing the hexadecimal class name.

Then call writeLong to write the serialized UID

Then there is a judgment that determines how the class interface is implemented, calling writeByte () to write a byte of flag bit.

Here are all the markers

/ * *

* Bit mask for ObjectStreamClass flag. Indicates Externalizable data

* written in Block Data mode.

* Added for PROTOCOL_VERSION_2.

*

* @ see # PROTOCOL_VERSION_2

* @ since 1.2

, /

Final static byte SC_BLOCK_DATA = 0x08

/ * *

* Bit mask for ObjectStreamClass flag. Indicates class is Serializable.

, /

Final static byte SC_SERIALIZABLE = 0x02

/ * *

* Bit mask for ObjectStreamClass flag. Indicates class is Externalizable.

, /

Final static byte SC_EXTERNALIZABLE = 0x04

/ * *

* Bit mask for ObjectStreamClass flag. Indicates class is an enum type.

* @ since 1.5

, /

Final static byte SC_ENUM = 0x10

Then call writeShort to write the field length of two bytes (for example, if there are three variables, write 0003)

Then there is a loop, ready to write the variable name of the class and its corresponding variable type

Each cycle:

WriteByte writes a byte variable type; writeUTF () writes the variable name to determine whether it is the original type, that is, if the object is not the original type (basic type), then call writeTypeString ()

This writeTypeString (), if it is a string, calls writeString ().

And this writeString () is often written like this, if the length of the string (not the size) is less than two bytes, first write a byte of TC_STRING (hexadecimal 74), then call writeUTF (), write a signature, which seems to be related to jvm, and finally write something like the following string

74 00 12 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b

The "translation" means that the string type is 18 bytes long, and the variable name is

Ljava/lang/string

And if we say that the above object has been declared before, that is, there is this string 74 00 12. 3b in front of it.

Then I will call writeHandle (), first write a byte of TC_REFERENCE (hexadecimal 71), and then call writeInt () to write 007e0000 + handle. This handle is the previously declared position of the object. Here, I haven't cleared up how the location is located, which is generally 0001, that is, writeHandle (), which is generally written as follows:

71 00 7e 00 XX such 5 bytes (the last 00 XX is not sure, I'll figure it out later, it's usually 00 01)

The above is over, that is, we have finished writing writeNonProxy (), and now we go back to writeNonProxyDesc () again.

Next, continue to call writeByte () to write a byte of TC_ENDBLOCKDATA (hexadecimal 78), the block end flag bit.

Then call writeCLassDesc (), and the parameter is the parent class of desc. If the parent class does not implement the serialization interface, it will not be written. Otherwise, go back to the step of writeNonProxyDesc and start writing the class information and variable information of the parent class (start bit 72, stop bit 78), which is similar to a recursive call. Finally, if the parent class of the serialization interface is not implemented, writeNull () will be called to write a byte of TC_NULL (hexadecimal 70). It means you don't have a date.

All right, anyway, after the recursive call of writeClassDesc () is finished, we go back to writeOrdinaryObject ().

Next, call writeSerialData (), ready to write serialized data

WriteSerialData ()

Private void writeSerialData (Object obj, ObjectStreamClass desc)

Throws IOException

{

ObjectStreamClass.ClassDataSlot [] slots = desc.getClassDataLayout ()

For (int I = 0; I < slots.length; iTunes +) {

ObjectStreamClass slotDesc = slotts [I] .desc

If (slotDesc.hasWriteObjectMethod ()) {

PutFieldImpl oldPut = curPut

CurPut = null

SerialCallbackContext oldContext = curContext

If (extendedDebugInfo) {

DebugInfoStack.push (

"custom writeObject data (class" +)

SlotDesc.getName () + ""))

}

Try {

CurContext = new SerialCallbackContext (obj, slotDesc)

Bout.setBlockDataMode (true)

SlotDesc.invokeWriteObject (obj, this)

Bout.setBlockDataMode (false)

Bout.writeByte (TC_ENDBLOCKDATA)

} finally {

CurContext.setUsed ()

CurContext = oldContext

If (extendedDebugInfo) {

DebugInfoStack.pop ()

}

}

CurPut = oldPut

} else {

DefaultWriteFields (obj, slotDesc)

}

}

}

A loop with an upper limit on the number of classes (including parent classes)

Each round:

Call defaultWriteFields ()

DefaultWriteFields ()

Private void defaultWriteFields (Object obj, ObjectStreamClass desc)

Throws IOException

{

Class cl = desc.forClass ()

If (cl! = null & & obj! = null & &! cl.isInstance (obj)) {

Throw new ClassCastException ()

}

Desc.checkDefaultSerialize ()

Int primDataSize = desc.getPrimDataSize ()

If (primVals = = null | | primVals.length < primDataSize) {

PrimVals = new byte [primDataSize]

}

Desc.getPrimFieldValues (obj, primVals)

Bout.write (primVals, 0, primDataSize, false)

ObjectStreamField [] fields = desc.getFields (false)

Object [] objVals = new Object [desc.getNumObjFields ()]

Int numPrimFields = fields.length-objVals.length

Desc.getObjFieldValues (obj, objVals)

For (int I = 0; I < objVals.length; iTunes +) {

If (extendedDebugInfo) {

DebugInfoStack.push (

"field (class"+ desc.getName () +", name:"+

Fields [numPrimFields + I] .getName () + ", type:"+

Fields [numPrimFields + I] .getType () + ")")

}

Try {

WriteObject0 (objVals [I]

Fields [numPrimFields + I] .isUnshared ()

} finally {

If (extendedDebugInfo) {

DebugInfoStack.pop ()

}

}

}

}

First determine whether it is a basic type, and if so, call write to write serialized data directly.

Otherwise, get the number of all variables in the class and start the loop

Each round of this cycle:

Call writeObject0 () to write the variable, that is, according to the variable type, with the appropriate method.

The final cycle ends.

With all the variables written, the first loop ends, the call to the writeSerialData () method finishes, it goes back to writeOrdinaryObject (), the execution ends back to writeObject0 (), and back to writeObject ().

On how to achieve serialization and deserialization in ava to share here, I hope that the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

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.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report