Java Basics - Detailed Explanation of Serializable Serialization

foreword

I believe that many small partners will implement Serializable on the class when writing the Java object class. But what exactly does it do?

Before understanding serialization, let's first understand why serialization is used.

text

The advent of serialization

Those who have done projects know that there are many projects based on microservice architecture in the market. After the service is split according to the business, although the decoupling of the business has been realized, many new problems have also appeared at the same time. For example, data atomicity, interface communication between services, etc.

What if several different services want to share the same data object at this time?

This is where serialization comes in . Taking the Java language as an example, we all know that Java belongs to an object programming language , which is a language for interacting with people . But the number 010101 in binary belongs to machine language and interacts with the CPU . Then if our Java objects want to be understood by the machine and turn them into " binary streams ", this process of converting encoding is called " serialization ". The process of decoding a binary stream into a Java object is called " deserialization ".

Here comes the point, object data sharing between different services . First, the object is serialized into a binary stream , then transmitted to another service through the network, and then its binary stream is decoded and converted into an object again.

One additional point: Serialization based on microservice architecture usually uses Json serialization instead of serialization provided by JDK.

JDK serialization

1. Usage scenarios

For a single project, in fact, we can use the serialization scenario only when the PO entity corresponding to the database table will be serialized. For example, when we use the Mybatis framework to insert objects into the database.

We can also simply understand it as: only serialize your objects when they need to be transmitted over the network .

2.serialVersionUID

After implementing Serializable, we often define a serialVsersionUID variable. Why? Let's take a look at the code and see what it does.

First create the following entities and implement serialization. At this time, I did not define the serialVsersionUID variable for it.

/**
 * <Description> ******
 *
 * @author yuSen.zhang
 * @version 1.0
 * @date 2023/02/27
 */
@Data
public class SerialTest implements Serializable {

    private String name;

    private Integer age;

    private String sex;

    @Override
    public String toString() {
        return "SerialTest{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }

    public static void serialize() throws IOException {
        SerialTest test = new SerialTest();
        test.setName("YiSenZ");
        test.setAge(11);
        test.setSex("男");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(new File("SerialTest.txt").toPath()));
        objectOutputStream.writeObject(test);
        objectOutputStream.close();
        System.out.println("序列化成功!生内容成文件到:SerialTest.txt");
    }

    public static void deserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream =
                new ObjectInputStream(Files.newInputStream(new File("SerialTest.txt").toPath()));
        Object read = objectInputStream.readObject();
        SerialTest test = new SerialTest();
        BeanUtils.copyProperties(read, test);
        objectInputStream.close();
        System.out.println("反序列化结果为:" + test);
    }

    @SneakyThrows
    public static void main(String[] args) {
        //执行序列化
        serialize();
        //执行反序列化
        deserialize();
    }
}

Let's take a look at SerialTest.txt (ignore its content, encoding format issues) and the results printed by the console

SerialTest.txt:
sr )com.epidemic.report.controller.SerialTest$taget java/lang/Integer;
L namet Ljava/lang/String;
L sexq ~ xpsr java.lang.Integer⠤÷8 I valuexr java.lang.Number˂  
xp   t YiSenZt 男

控制台:
序列化成功!生内容成文件到:SerialTest.txt
反序列化结果为:SerialTest(name=YiSenZ, age=11, sex=男)

 It can be seen that both serialization and deserialization are normal at this time.

So at this time, if we add a field to the entity and then deserialize it, will there be any problems?

    //新增一个测试用的字段
    private String addField;

    @SneakyThrows
    public static void main(String[] args) {
        //直接执行反序列化
        deserialize();
    }
Exception in thread "main" java.io.InvalidClassException: com.epidemic.report.controller.SerialTest; local class incompatible: stream classdesc serialVersionUID = 2594804517751749556, local class serialVersionUID = 2578299843277314095
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2028)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1875)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2209)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1692)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:508)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:466)
	at com.epidemic.report.controller.SerialTest.deserialize(SerialTest.java:48)
	at com.epidemic.report.controller.SerialTest.main(SerialTest.java:60)

It can be seen that if we deserialize the previously serialized file at this time, the serial number will be different. This is because if we implement Serializable, it will automatically add a serialVsersionUID for us. Therefore we can conclude that:

1.serialVsersionUID it is same before and after object serialization .

2. If serialVsersionUID is not defined, the compiler will automatically declare it .

Of course, we can also understand serialVsersionUID as a lock, and the object is a door. When you go out (serialization), you lock the door (object). Then of course you have to use the corresponding key when you come back to open the door (deserialization).

Note: Fields modified by static will not be serialized. Because serialization saves the state of the object rather than the state of the class. So it ignores static.

3. Defects in JDK serialization

In the above practical operation, we can see that serialization is to convert the object state into a binary stream for operation. What if this stream is stolen and modified? Yes, this will lead to security problems, which is also officially described by Java: deserialization of untrusted data is inherently dangerous and should be avoided.

In addition, Java serialization is currently only applicable to Java-based implementation frameworks . If other languages ​​do not integrate Java serialization protocols, they cannot communicate with each other.

In addition, the performance of serialization is relatively low. In the process of network communication, performance is often an important indicator. For details, see the following comparison of ByteBuffer and JDK serialization efficiency.

@Data
@ToString
public class SerialTest implements Serializable {

    private String name;

    private Integer age;

    private String sex;

    @SneakyThrows
    public static void jdkSerializeTest() {
        SerialTest test = new SerialTest();
        test.setName("ObjectOutputStream");
        test.setAge(18);
        test.setSex("男");
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 500; i++) {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ObjectOutputStream outputStream = new ObjectOutputStream(os);
            outputStream.writeObject(test);
            outputStream.flush();
            outputStream.close();
            byte[] bytes = os.toByteArray();
            os.close();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("ObjectOutputStream序列化时间:" + (endTime - startTime));
    }

    @SneakyThrows
    public static void byteBufferTest() {
        SerialTest test = new SerialTest();
        test.setName("ByteBuffer");
        test.setSex("男");
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 500; i++) {
            ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
            byte[] uName = test.getName().getBytes(StandardCharsets.UTF_8);
            byte[] uSex = test.getSex().getBytes(StandardCharsets.UTF_8);
            byteBuffer.putInt(uName.length);
            byteBuffer.put(uName);
            byteBuffer.putInt(uSex.length);
            byteBuffer.put(uSex);
            byte[] bytes = new byte[byteBuffer.remaining()];
        }
        long endTime = System.currentTimeMillis();
        System.out.println("ByteBuffer序列化时间:" + (endTime - startTime));
    }

    @SneakyThrows
    public static void main(String[] args) {
        //JDK序列化
        jdkSerializeTest();
        //ByteBuffer序列化
        byteBufferTest();
    }
}
ObjectOutputStream序列化时间:52
ByteBuffer序列化时间:5

Guess you like

Origin blog.csdn.net/qq_33351639/article/details/129237233