Conocimiento profundo de —————— serialización y deserialización


Recientemente, estoy haciendo un proyecto de gestión de supermercados, almacenando datos en la base de datos de Redis a través de la serialización. Al implementar la función de modificar la información del usuario, con el fin de facilitar la modificación del tipo de dato Date a tipo String, se reportó un error, luego de abrir la consola ocurrió el siguiente error.

java.io.InvalidClassException: com.li.pojo.SmbmsUser; 
local class incompatible: stream classdesc serialVersionUID = 2416888619525883151, 
local class serialVersionUID = 516098953370879925

Causa del error: ID inconsistente antes y después de la serialización y deserialización resultó en un error.
Ahora que hay un error, debe resolverse. La comprensión anterior de la serialización y deserialización simplemente se mantuvo sobre la base de
saber cómo usarla. Aprendamos más hoy.

1. Primero, sepa qué son la serialización y la deserialización

Al aprender nuevos puntos de conocimiento, siempre debemos tener preguntas: qué, por qué, cómo hacerlo y tres aspectos para lograrlo. Solo entonces será claro y claro, y el recuerdo será más profundo.

  • ¿Qué es serialización y deserialización?

    Serialización: es convertir el objeto en una secuencia de caracteres
    Deserialización: es restaurar la secuencia de caracteres al objeto original

  • ¿Por qué serialización y deserialización?
    Como se mencionó anteriormente, al hacer proyectos ssm, la serialización se usó para convertir objetos en secuencias de caracteres y se almacenaron en la base de datos de Redis
    . ¿Qué debemos hacer si no se usa la serialización? Use el objeto de almacenamiento map (key -value), se puede utilizar cuando no hay demasiados datos en una sola tabla y la cantidad de datos es muy problemática. Por lo tanto, se recomienda almacenar objetos serializados en Redis cuando la cantidad de datos es particularmente grande.
    Generalmente, la serialización se usa para escribir en bases de datos, archivos y para transmisión de red.

  • ¿Qué puedo hacer para serializarlo?

Implementar interfaz serializable

public interface Serializable {
    
    
}

Serializable es claramente una interfaz vacía, de hecho, la función de esta interfaz es decirle a la JVM que quiero serializar y deserializar.
La operación específica es la siguiente:
Preparar la clase de entidad

public class Student {
    
    
    private String name;
    private int age;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

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

prueba:

 //初始化
        Student student = new Student();
        student.setName("张三");
        student.setAge(12);
        System.out.println(student.toString());
        //序列化
        try {
    
    
            //把对象写入文件中
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\imag\\b.txt"));
            oos.writeObject(student);
            System.out.println("序列化成功!");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }

        //反序列化
        try {
    
    
            //从文件中读出对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\imag\\b.txt"));
            Student stu = (Student) ois.readObject();
            System.out.println("反序列化成功!");
            System.out.println(stu.toString());

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

La interfaz serializable no está implementada y se informa de un error.

java.io.NotSerializableException: com.li.pojo.Student
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at com.li.test.SerializableTest.serializable(SerializableTest.java:25)
	at com.li.test.SerializableTest.main(SerializableTest.java:14)

Implementar interfaz serializable

public class Student implements Serializable{
    
    
	//省略属性
	//省略setter、getter方法
	//省略toString方法
}

Imprima el objeto antes de la serialización:

Estudiante {nombre = 'Zhang San', edad = 12}

¡La serialización es exitosa! Y b.txt tiene algo de contenido

Inserte la descripción de la imagen aquí
Luego, llame a la deserialización y la
deserialización del objeto de salida será exitosa.

Estudiante {nombre = 'Zhang San', edad = 12}

Después de la deserialización, se descubrió que era coherente con el objeto antes de la serialización.

2. El proceso operativo específico de serialización y deserialización

¿Cómo se realizan la serialización y deserialización específicas?

  • El proceso de ejecución de serialización específico es el siguiente (se omite la lista de parámetros):

ObjectOutputStream-> writeObject () -> writeObject0 () -> writeOrdinaryObject () -> writeSerialData () -> defaultWriteFields ()
El método writeObject0 juzga
si es un tipo String, un tipo de enumeración, un objeto serializado, si no lo es, arrojará ¿Es la excepción
NotSerializableException muy familiar? La excepción de que no implementamos la interfaz Serializable lanzada es de aquí.

			 // 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());
                }
            }
  • El proceso de ejecución de deserialización específico es el siguiente (se omite la lista de parámetros):
    ObjectInputStream -> readObject () -> readObject0 () -> readOrdinaryObject () -> readSerialData () -> defaultReadFields ()

3. ¿Por qué no se pueden serializar las propiedades modificadas por transitorios y estáticos?

Pruebe dos palabras clave:

  • estático
  • transitorio
	//新增加两个字段
    public static String addRess="宁夏";
    private transient String carName="捷豹";
     @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", addRess='" + addRess + '\'' +
                ", carName='" + carName + '\''+
                '}';
    }
 		//初始化
        Student student = new Student();
        student.setName("张三");
        student.setAge(12);
        System.out.println(student.toString());
        //序列化
        try {
    
    
            //把对象写入文件中
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\imag\\b.txt"));
            oos.writeObject(student);
            System.out.println("序列化成功!");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }

        student.setAddRess("陕西");

        //反序列化
        try {
    
    
            //从文件中读出对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\imag\\b.txt"));
            Student stu = (Student) ois.readObject();
            System.out.println("反序列化成功!");
            System.out.println(stu.toString());

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

Resultado de la impresión de salida:

//序列化前的对象
Student{
    
    name='张三', age=12, addRess='宁夏', carName='捷豹'}
序列化成功!
反序列化成功!
//反序列化后的对象
Student{
    
    name='张三', age=12, addRess='陕西', carName='null'}

A partir de los resultados, podemos comparar:
Antes de serialización: addRessel valor del '宁夏'campo '陕西', modificar el valor del campo después de la serialización, y el valor del campo después de deserialización '陕西', no el estado antes de deserialización.
¿Por qué? El campo modificado estático original pertenece al estado de la clase y la serialización es para el estado del objeto, por lo que la palabra clave modificada por la palabra clave estática no guarda el estado
antes de la serialización. carNameEl valor del campo antes de la ''捷豹serialización y el valor del campo después de la serialización son null. ¿Por qué?
El campo original transitorio (temporal) modificado por palabra clave puede evitar que el campo se serialice en el archivo. Después de ser deserializado, el valor del campo transitorio se establece en el valor inicial. Por ejemplo, el valor inicial del tipo int es 0 y el tipo de objeto El valor inicial de es nulo.

Si desea explorar, también se refleja en el código fuente.

/**
     * Returns array of ObjectStreamFields corresponding to all non-static
     * non-transient fields declared by given class.  Each ObjectStreamField
     * contains a Field object for the field it represents.  If no default
     * serializable fields exist, NO_FIELDS is returned.
     */
 private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
    
    
        Field[] clFields = cl.getDeclaredFields();
        ArrayList<ObjectStreamField> list = new ArrayList<>();
        int mask = Modifier.STATIC | Modifier.TRANSIENT;

        for (int i = 0; i < clFields.length; i++) {
    
    
            if ((clFields[i].getModifiers() & mask) == 0) {
    
    
                list.add(new ObjectStreamField(clFields[i], false, true));
            }
        }
        int size = list.size();
        return (size == 0) ? NO_FIELDS :
            list.toArray(new ObjectStreamField[size]);
    }

Se explica en los comentarios: Modifier.STATIC | Modifier.TRANSIENTlos campos estáticos y temporales no se serializarán

4. ¿Cuál es la función de la identificación serializada?

No sé si los amigos han descubierto que todas las clases que implementan la interfaz serializable tendrán la siguiente línea de código
private static final long serialVersionUID = 5791892247841471821L

serialVersionUID: el significado del ID de serialización
Entonces, ¿cuál es el rol de este ID de serialización?
De hecho, es para asegurar que la deserialización pueda tener éxito.
Si no establece el ID de serialización después de que se implemente la interfaz de serialización, Java generará un ID de serialización para usted (generado de acuerdo con la cantidad de tipos de atributos de campo)
pero si que está serializando Después de la conversión, agregar un determinado campo reportará un error.
Verifiquémoslo:

	//准备两个字段
     private String name;
     private int age;
     //提供setter、getter方法 

Prueba:
serialización

    //初始化
        Student student = new Student();
        student.setName("张三");
        student.setAge(12);
        System.out.println(student.toString());
        //序列化
        try {
    
    
            //把对象写入文件中
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\imag\\b.txt"));
            oos.writeObject(student);
            System.out.println("序列化成功!");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        //反序列化
        try {
    
    
            //从文件中读出对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\imag\\b.txt"));
            Student stu = (Student) ois.readObject();
            System.out.println("反序列化成功!");
            System.out.println(stu.toString());

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

Resultado de la impresión:

Estudiante {nombre = '张三', edad = 12} ¡
serializado exitosamente! ¡
Deserializado exitosamente!
Estudiante {nombre = '张三', edad = 12}

Modifique ageel tipo de datos del campo a tipo de cadena. Para
esta prueba, primero serialicemos.

//初始化
		Student student = new Student();
        student.setName("张三");
        student.setAge(12);
        System.out.println(student.toString());
        //序列化
        try {
    
    
            //把对象写入文件中
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\imag\\b.txt"));
            oos.writeObject(student);
            System.out.println("序列化成功!");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }

Luego, modifique el campo de la clase de entidad al tipo String, comente el código de serialización, realice la deserialización
e informe un error:

java.io.InvalidClassException: com.li.pojo.Student; local class incompatible: stream classdesc serialVersionUID = -5791892247841471821, local class serialVersionUID = -3882745368779827342
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)

Este error es el mismo que dije al principio. El ID de serialización y el ID de deserialización son inconsistentes.
Por lo tanto, generalmente solo necesitamos implementar la interfaz de serialización y usar el ID de serialización predeterminado (1L) para ser
privado, estático, final, serial largo, UID = 1L

Supongo que te gusta

Origin blog.csdn.net/lirui1212/article/details/109514723
Recomendado
Clasificación