Conhecimento profundo de —————— serialização e desserialização


Recentemente, estou fazendo um projeto de gerenciamento de supermercado, armazenando dados no banco de dados Redis por meio de serialização. Ao implementar a função de modificação das informações do usuário, a fim de facilitar a modificação do tipo de dado Date para o tipo String, foi reportado um erro.Após a abertura do console, ocorreu o seguinte erro.

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

Causa do erro: ID inconsistente antes e depois da serialização e a desserialização resultou em um erro.
Agora que há um erro, ele deve ser resolvido. O entendimento anterior de serialização e desserialização apenas se baseava em
saber como usá-lo. Vamos aprender mais hoje.

1. Primeiro saiba o que são serialização e desserialização

Ao aprender novos pontos de conhecimento, devemos sempre ter dúvidas: o que, por que, como fazer e três aspectos para conseguir isso. Só então ficará claro e claro, e a memória será mais profunda.

  • O que é serialização e desserialização?

    Serialização: é converter o objeto em uma seqüência de caracteres.
    Desserialização: é restaurar a seqüência de caracteres para o objeto original

  • Por que serialização e desserialização?
    Conforme mencionado anteriormente, ao fazer projetos ssm, a serialização era usada para converter objetos em sequências de caracteres e armazenados no banco de dados Redis
    . O que devemos fazer se a serialização não for usada, use o objeto de armazenamento map (key -value), pode ser usado quando não há muitos dados em uma única tabela e a quantidade de dados é muito problemática. Portanto, é recomendável armazenar objetos serializados no Redis quando a quantidade de dados for particularmente
    grande.Geralmente, a serialização é usada para ser gravada em bancos de dados, arquivos e usada para transmissão de rede.

  • O que posso fazer para serializá-lo?

Implementar interface serializável

public interface Serializable {
    
    
}

Serializável é claramente uma interface vazia. Na verdade, a função dessa interface é informar à JVM que desejo serializar e desserializar.
A operação específica é a seguinte:
Prepare a classe de entidade

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 +
                '}';
    }
}

teste:

 //初始化
        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();
        }

A interface serializável não está implementada e um erro é relatado.

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 interface serializável

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

Imprima o objeto antes da serialização:

Aluno {nome = 'Zhang San', idade = 12}

A serialização foi bem-sucedida! E b.txt tem algum conteúdo

Insira a descrição da imagem aqui
Em seguida, chame a desserialização e a
desserialização do objeto de saída será bem-sucedida!

Aluno {nome = 'Zhang San', idade = 12}

Após a desserialização, ele era consistente com o objeto antes da serialização!

2. O processo de operação específico de serialização e desserialização

Como a serialização e a desserialização específicas são realizadas?

  • O processo de execução de serialização específico é o seguinte (a lista de parâmetros é omitida):

ObjectOutputStream-> writeObject () -> writeObject0 () -> writeOrdinaryObject () -> writeSerialData () -> defaultWriteFields ()
O método writeObject0 julga
se é um tipo String, um tipo de enumeração, um objeto serializado, se não for, ele lançará A exceção
NotSerializableException é muito familiar? A exceção de que não implementamos a interface Serializable lançada é daqui.

			 // 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());
                }
            }
  • O processo de execução de desserialização específico é o seguinte (a lista de parâmetros é omitida):
    ObjectInputStream -> readObject () -> readObject0 () -> readOrdinaryObject () -> readSerialData () -> defaultReadFields ()

3. Por que as propriedades modificadas por transiente e estático não podem ser serializadas?

Teste duas palavras-chave:

  • estático
  • transitório
	//新增加两个字段
    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 impressão de saída:

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

A partir dos resultados, podemos comparar:
Antes de serialização: addResso valor do '宁夏'campo '陕西', modificar o valor do campo após a serialização, e o valor do campo após a desserialização '陕西', não o estado antes desserialização.
Por quê? O campo modificado estático original pertence ao estado da classe e a serialização é para o estado do objeto, portanto, a palavra-chave modificada pela palavra-chave estática não salva o estado
antes da serialização. carNameO valor do campo antes da ''捷豹serialização e o valor do campo após a serialização são null. Por quê?
O campo transiente original (temporário) modificado por palavra-chave pode impedir que o campo seja serializado no arquivo. Depois de ser desserializado, o valor do campo transiente é definido como o valor inicial. Por exemplo, o valor inicial do tipo int é 0 e o tipo de objeto O valor inicial de é nulo.

Se você quiser explorar, isso também se reflete no código-fonte

/**
     * 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]);
    }

Isso é explicado nos comentários: Modifier.STATIC | Modifier.TRANSIENTcampos estáticos e temporários não serão serializados

4. Qual é a função do ID serializado

Não sei se os amigos descobriram que todas as classes que implementam a interface serializável terão a seguinte linha de código
private static final long serialVersionUID = 5791892247841471821L

serialVersionUID: o significado do ID de serialização
Então, qual é a função deste ID de serialização?
Na verdade, é para garantir que a desserialização possa ser bem-sucedida.
Se você não definir o ID de serialização após a implementação da interface de serialização, o java irá gerar um ID de serialização para você (gerado de acordo com o número de tipos de atributos de campo),
mas se você está serializando Após a conversão, adicionar um determinado campo reportará um erro.
Vamos verificá-lo:

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

Teste:
serialização

    //初始化
        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 da impressão:

Aluno {name = '张三', idade = 12}
Serializado com sucesso!
Deserializado com sucesso!
Aluno {name = '张三', idade = 12}

Modifique ageo tipo de dados do campo para o tipo String. Para
este teste, vamos serializá-lo primeiro.

//初始化
		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();
        }

Em seguida, modifique o campo da classe de entidade para o tipo String, comente o código de serialização, execute a desserialização
e relate um erro:

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 erro é o mesmo que eu disse no início. O ID de serialização e o ID de desserialização são inconsistentes.
Geralmente, só precisamos implementar a interface de serialização e usar o ID de serialização padrão (1L) para ser
private static final long serialVersionUID = 1L

Acho que você gosta

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