What is the function of transient keyword

Time:2021-10-13

1. From serilizable to transient

We know that if an object needs to be serialized, it needs to be implementedSerilizableInterface, then all non static properties of this class will be serialized.

Note: it saysNon static attribute, because static attributes belong to classes, not class objects, and serialization is an operation on class objects, this will not be serialized at all. Now we can experiment:
Entity classTeacher.class:

import java.io.Serializable;

class Teacher implements Serializable {
    public int age;
    public static String schoolName;

    public Teacher(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                '}';
    }
}

Test codeSerialTest.java, the basic idea is to initialize static attributesschoolNameFor “Dongfang primary school”, after serializing the object, modify the static attribute, and then deserialize it. It is found that the static variable is still modified, indicating that the static variable has not been serialized.

import java.io.*;

public class SerialTest {
    public static void main(String[] args) {
        Teacher.schoolname = "Oriental primary school";
        serial();
        Teacher.schoolname = "western primary school";
        deserial();
        System.out.println(Teacher.schoolName);
    }
    //Serialization
    private static void serial(){
        try {
            Teacher teacher = new Teacher(9);
            FileOutputStream fileOutputStream = new FileOutputStream("Teacher.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(teacher);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
    //Deserialization
    private static void deserial() {
        try {
            FileInputStream fis = new FileInputStream("Teacher.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Teacher teacher = (Teacher) ois.readObject();
            ois.close();
            System.out.println(teacher.toString());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

The output results are provedStatic variable is not serialized!!!

Teacher{age=9}
Western primary school

2. The class that serializes the attribute object needs to implement the serializable interface?

I suddenly thought of a question. If some attributes are objects rather than basic types, do you need to change the type of attributesSerilizableAnd?

The answer to the question is: Yes!!!

The following is the experimental process:

First, there is oneTeacher.java, implementedSerializable, there is an attributeSchoolType:

import java.io.Serializable;

class Teacher implements Serializable {
    public int age;
    public School school;
  
    public Teacher(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                '}';
    }
}

SchoolType, not implementedSerializable:

public class School {
    public String name;

    public School(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                '}';
    }
}

To test the code, we only test serialization:

import java.io.*;

public class SerialTest {
    public static void main(String[] args) {
        serial();
    }

    private static void serial(){
        try {
            Teacher teacher = new Teacher(9);
            Teacher.school = new school ("Oriental primary school");
            FileOutputStream fileOutputStream = new FileOutputStream("Teacher.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(teacher);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

You will find that an error is reported. The reasons for the error are:School cannot be serialized, that is, it does not implement the serialization interfaceTherefore, if we want to serialize an object, the properties of the object must also be serializable, orIt istransientDecorated.

java.io.NotSerializableException: com.aphysia.transienttest.School
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at com.aphysia.transienttest.SerialTest.serial(SerialTest.java:18)
    at com.aphysia.transienttest.SerialTest.main(SerialTest.java:9)

When we willSchoolWhen implementing the serialization interface, it is found that everything is normal… The problem is solved perfectly

3. What about fields that do not want to be serialized?

However, if a variable is not a static variable, but we do not want to serialize it, because it may be some sensitive fields such as passwords, or it is a less important field. We do not want to increase the message size, so we want to exclude this field from the serialized message. Or change the field to store the reference address, not the really important data, such asArrayListInsideelementData

It needs to be used at this timetransientKeyword, the field will be masked.

When we usetransientmodificationSchoolWhen:

import java.io.Serializable;

class Teacher implements Serializable {
    public int age;
    public transient School school;
    public Teacher(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", school=" + school +
                '}';
    }
}
import java.io.Serializable;

public class School implements Serializable {
    public String name;

    public School(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                '}';
    }
}

Execute the following code for serialization and deserialization:

import java.io.*;

public class SerialTest {
    public static void main(String[] args) {
        serial();
        deserial();
    }

    private static void serial(){
        try {
            Teacher teacher = new Teacher(9);
            Teacher.school = new school ("Oriental primary school");
            FileOutputStream fileOutputStream = new FileOutputStream("Teacher.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(teacher);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    private static void deserial() {
        try {
            FileInputStream fis = new FileInputStream("Teacher.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Teacher teacher = (Teacher) ois.readObject();
            ois.close();
            System.out.println(teacher.toString());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

The execution results are as follows:SchoolThe field is deserialized, which is actually nulltransientIt worked.
But notice,transientOnly variables can be modified, but classes and methods cannot be modified,

Teacher{age=9,school=null}

4. The elementdata in ArrayList is modified by the transient keyword. Why can ArrayList be serialized?

Here, sincetransientDecoratedArrayListSo why can we still see when serializingArrayListWhat about your data nodes?
This is because when serializing:

If onlySerializableInterface, when serializing, it must be calledjava.io.ObjectOutputStream.defaultWriteObject()Method to serialize the object. And if sotransientIf the attribute is modified, it must not be serialized.
But if we doSerializableInterface, alsotransientDecorated this attribute, which will not be in the defaultjava.io.ObjectOutputStream.defaultWriteObject()Method, but ArrayList overrides onewriteObject()Method, which is called when serializingArrayListRewrittenwriteObject()Method, notjava.io.ObjectOutputStream.defaultWriteObject()The way to do it.

The following source code isObjectOutputStream.writeObject(Object obj)In fact, the underlying layer will call the rewritten object in a reflective waywriteObject()Method, there is no expansion here.

public final void writeObject(Object obj) throws IOException {
        //If it can be overridden, the overridden method is called
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0) {
                writeFatalException(ex);
            }
            throw ex;
        }
    }

ArrayListRewrittenwriteOject()The method is as follows:

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        //Call the method of the default serialized object to realize the normal serialization operation
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        //At this time, elementdata is not serialized in the default serialization method, and the writeobject operation is performed on the elements in elementdata separately
        for (int i=0; i

As we can see,writeOject()In fact, the default method is called insidedefaultWriteObject()defaultWriteObject()The bottom layer is actually changedwriteObject0()method.ArrayListRewrittenwriteOject()The main idea is to serialize the default, then serialize the array size, and then serialize the arrayelementDataThe real elements inside. This achieves the purpose of serializing the real content of the element.

/**
     * Reconstitute the ArrayList instance from a stream (that is,
     * deserialize it).
     */
    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
            int capacity = calculateCapacity(elementData, size);
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i

ArrayListThe deserialization operation of is shown in the above code. First call defaultreadobject() to perform the default deserialization operation. The real elements in the elementdata written by writeobject() are read out one by one in the for loop to realize the deserialization of elementdata. Therefore, although elementdata is decorated with the transient keyword, because the writeobject() and readobject() methods are rewritten, Therefore, the serialization and deserialization of the elementdata attribute are finally implemented.

Similarly, borrowArrayListThe implementation logic of can realize the serialization and deserialization of attributes with transient keyword. Here is a simple entity class

class TransientTest implements Serializable {
    private static final long serialVersionUID = 233858934995755239L;
    private String name1;
    private transient String name2;

    public TransientTest(String name1, String name2) {
        this.name1 = name1;
        this.name2 = name2;
    }

    public String toString() {
        return String.format("TransientTest.toString(): name1=%s,name2=%s", name1, name2);
    }

    private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
        s.defaultWriteObject();
        s.writeObject(name2);
    }

    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        name2 = String.valueOf(s.readObject());
    }

    private static void serial() {
        try {
            Transient test teacher = new transient test ("general attribute", "transient modified attribute");
            System. Out. Println ("before serialization:" + teacher. Tostring());
            FileOutputStream fileOutputStream = new FileOutputStream("Teacher.txt");
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(teacher);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    private static void deserial() {
        try {
            FileInputStream fis = new FileInputStream("Teacher.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            TransientTest teacher = (TransientTest) ois.readObject();
            ois.close();
            System. Out. Println ("deserialization:" + teacher. Tostring());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        serial();
        deserial();
    }
}

The results are as follows:

Before serialization: transienttest. Tostring(): name1 = general attribute, Name2 = modified attribute
Deserialization: transienttest. Tostring(): name1 = general attribute, Name2 = modified attribute

5. Other than transient, is there any other way to mask the deserialization of attributes?

Wait a minute, ask this question, the answer must be there!!! That’s itExternalizableInterface.

Details:ExternalizableIt means that there are many attributes in the class, but I only want some and shield most, so I don’t want to add keywords in front of most attributestransient, I just want to identify my serialized fields, which I need to use at this timeExternalizableInterface.

First define aPerson.java, there are three properties

  • Age: age
  • Name: name (by)transientModification)
  • Score: Score

RealizedExternalizableInterface, it must be implementedwriteExternal()andreadExternal()method.

  • Writeexternal: Custom serialize the attributes to be serialized
  • Readexternal: Custom deserialization of attributes to be deserialized
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Person implements Externalizable {
    public int age;
    public transient String name;
    public int score;

    //A parameterless constructor must be implemented
    public Person() {
    }

    public Person(int age, String name, int score) {
        this.age = age;
        this.name = name;
        this.score = score;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        /*
         *Specifies the property to write when serializing. Score is not written here
         */
        out.writeObject(age);
        out.writeObject(name);
        out.writeObject(score);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        /*
         *Specifies the property to write when serializing. Age is still not written here
         */
        this.age = (int)in.readObject();
        this.name = (String)in.readObject();
    }

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

From the above code, we can see that all three attributes are written in during serialization, but only two attributes are restored during deserialization. Let’s take a look at the test code:

import java.io.*;

public class ExternalizableTest {
    public static void main(String[] args) {
        serial();
        deserial();
    }

    private static void serial(){
        try {
            Person person = new Person(9,"Sam",98);
            FileOutputStream fileOutputStream = new FileOutputStream("person.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(person);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    private static void deserial() {
        try {
            FileInputStream fis = new FileInputStream("person.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Person person = (Person) ois.readObject();
            ois.close();
            System.out.println(person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

The test results are as follows. In fact, the first two are deserialized successfully. The latter one is because we didn’t customize the deserialization of the attribute when rewriting, so it’s normal

Person{age=9, name='Sam', score='0'}

If you are more careful, you can find that one field istransientModified, doesn’t it mean that after modification, it won’t be serialized? How can it be serialized.

Yes, as long as it is realizedExternalizableInterface, in fact, will not betransientAbout, we will only serialize and deserialize according to our custom fields. HeretransientIs invalid

Standing on the shoulders of giants picking apples

https://www.jianshu.com/p/31c77d833a8e

https://blog.csdn.net/u010188178/article/details/83581506

Recommended Today

The selector returned by ngrx store createselector performs one-step debugging of fetching logic

Test source code: import { Component } from ‘@angular/core’; import { createSelector } from ‘@ngrx/store’; export interface State { counter1: number; counter2: number; } export const selectCounter1 = (state: State) => state.counter1; export const selectCounter2 = (state: State) => state.counter2; export const selectTotal = createSelector( selectCounter1, selectCounter2, (counter1, counter2) => counter1 + counter2 ); // […]