CodeGym/Blogue Java/Random-PT/Qual é a diferença entre serialização e desserialização e...
John Squirrels
Nível 41
San Francisco

Qual é a diferença entre serialização e desserialização em Java?

Publicado no grupo Random-PT
Oi! Na lição de hoje, falamos sobre serialização e desserialização em Java. Começaremos com um exemplo simples. Digamos que você criou um jogo de computador. Se você cresceu nos anos 90 e se lembra dos consoles de jogos daquela época, provavelmente sabe que eles careciam de algo que consideramos natural hoje - a capacidade de salvar e carregar jogos :) Se não, imagine isso! Qual é a diferença entre serialização e desserialização em Java?  - 1 Receio que hoje um jogo sem essas habilidades estaria condenado! O que significa "salvar" e "carregar" um jogo? Bem, entendemos o significado comum: queremos continuar o jogo de onde paramos. Para fazer isso, criamos uma espécie de "ponto de verificação", que usamos para carregar o jogo. Mas o que isso significa para um programador e não para um jogador casual? A resposta é simples: nós'. Digamos que você esteja jogando com a Espanha no Strategium. Seu jogo tem um estado: quem possui quais territórios, quem tem quantos recursos, quem está em aliança com quem, quem está em guerra com quem e assim por diante. Devemos de alguma forma salvar esta informação, o estado do nosso programa, para restaurá-lo no futuro e continuar o jogo. Pois é exatamente para isso que servem a serialização e a desserealização . A serialização é o processo de armazenar o estado de um objeto em uma sequência de bytes. Desserializaçãoé o processo de restauração de um objeto desses bytes. Qualquer objeto Java pode ser convertido em uma sequência de bytes. Por que precisaríamos disso? Dissemos mais de uma vez que os programas não existem por conta própria. Na maioria das vezes, eles interagem com outros programas, trocam dados etc. E uma sequência de bytes é um formato conveniente e eficiente. Por exemplo, podemos transformar nosso SavedGameobjeto em uma sequência de bytes, enviar esses bytes pela rede para outro computador e, no segundo computador, transformar esses bytes de volta em um objeto Java! Parece difícil, certo? E implementar esse processo parece uma dor de cabeça :/ Felizmente, não é assim! :) Em Java, oSerializableinterface é responsável pelo processo de serialização. Essa interface é extremamente simples: você não precisa implementar um único método para usá-la! É assim que nossa classe de salvamento de jogo parece simples:
import java.io.Serializable;
import java.util.Arrays;

public class SavedGame implements Serializable {

   private static final long serialVersionUID = 1L;

   private String[] territoriesInfo;
   private String[] resourcesInfo;
   private String[] diplomacyInfo;

   public SavedGame(String[] territoriesInfo, String[] resourcesInfo, String[] diplomacyInfo){
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public String[] getTerritoriesInfo() {
       return territoriesInfo;
   }

   public void setTerritoriesInfo(String[] territoriesInfo) {
       this.territoriesInfo = territoriesInfo;
   }

   public String[] getResourcesInfo() {
       return resourcesInfo;
   }

   public void setResourcesInfo(String[] resourcesInfo) {
       this.resourcesInfo = resourcesInfo;
   }

   public String[] getDiplomacyInfo() {
       return diplomacyInfo;
   }

   public void setDiplomacyInfo(String[] diplomacyInfo) {
       this.diplomacyInfo = diplomacyInfo;
   }

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoriesInfo=" + Arrays.toString(territoriesInfo) +
               ", resourcesInfo=" + Arrays.toString(resourcesInfo) +
               ", diplomacyInfo=" + Arrays.toString(diplomacyInfo) +
               '}';
   }
}
As três matrizes são responsáveis ​​por informações sobre territórios, recursos e diplomacia. A interface Serializable informa à máquina virtual Java: " Está tudo bem — se necessário, os objetos desta classe podem ser serializados ". Uma interface sem uma única interface parece estranha :/ Por que é necessário? A resposta para essa pergunta pode ser vista acima: serve apenas para fornecer as informações necessárias à máquina virtual Java. Em uma de nossas lições anteriores, mencionamos brevemente as interfaces de marcadores . Essas são interfaces informativas especiais que simplesmente marcam nossas classes com informações adicionais que serão úteis para a máquina Java no futuro. Eles não têm nenhum método que você precise implementar.Serializableé uma dessas interfaces. Outro ponto importante: Por que precisamos da private static final long serialVersionUIDvariável que definimos na classe? Por que é necessário? Este campo contém um identificador exclusivo para a versão da classe serializada . Qualquer classe que implementa a Serializableinterface possui um versionidentificador. É calculado com base no conteúdo da classe: seus campos, a ordem em que são declarados, métodos, etc. Se mudarmos o tipo de campo e/ou o número de campos em nossa classe, o identificador de versão muda imediatamente . serialVersionUIDtambém é escrito quando a classe é serializada. Quando tentamos desserializar, ou seja, restaurar um objeto a partir de um conjunto de bytes, o associado serialVersionUIDé comparado com o valor deserialVersionUIDpara a classe em nosso programa. Se os valores não corresponderem, um java.io. InvalidClassException será lançada. Veremos um exemplo disso a seguir. Para evitar isso, simplesmente definimos o identificador de versão manualmente em nossa classe. No nosso caso, será simplesmente igual a 1 (mas você pode substituir por qualquer outro número que desejar). Bem, é hora de tentar serializar nosso SavedGameobjeto e ver o que acontece!
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

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

       // Create our object
       String[] territoryInfo = {"Spain has 6 provinces", "Russia has 10 provinces", "France has 8 provinces"};
       String[] resourcesInfo = {"Spain has 100 gold", "Russia has 80 gold", "France has 90 gold"};
       String[] diplomacyInfo = {"France is at war with Russia, Spain has taken a neutral position"};

       SavedGame savedGame = new SavedGame(territoryInfo, resourcesInfo, diplomacyInfo);

       // Create 2 streams to serialize the object and save it to a file
       FileOutputStream outputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

       // Save the game to a file
       objectOutputStream.writeObject(savedGame);

       // Close the stream and free resources
       objectOutputStream.close();
   }
}
Como você pode ver, criamos 2 streams: FileOutputStreame ObjectOutputStream. O primeiro pode gravar dados em um arquivo e o segundo converte objetos em bytes. Você já viu construções "aninhadas" semelhantes, por exemplo, new BufferedReader(new InputStreamReader(...)), em lições anteriores, então elas não devem assustá-lo :) Ao criar uma "cadeia" de dois fluxos, realizamos ambas as tarefas: convertemos o SavedGameobjeto em um conjunto de bytes e salve-o em um arquivo usando o writeObject()método. E, aliás, nem olhamos o que conseguimos! É hora de olhar para o arquivo! *Nota: você não precisa criar o arquivo com antecedência. Caso não exista um arquivo com esse nome, ele será criado automaticamente* E aqui está o seu conteúdo!
¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;­ТVзй{G xp t pФранция воюет СЃ Россией, Испания заняла позицию нейтралитетаuq ~ t "РЈ Испании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Испании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
Uh-oh :( Parece que nosso programa não funcionou :( Na verdade, funcionou. Você se lembra que enviamos um conjunto de bytes, não apenas um objeto ou texto, para o arquivo? Bem, é isso que conjunto de bytes se parece :) Este é o nosso jogo salvo! Se quisermos restaurar nosso objeto original, ou seja, iniciar e continuar o jogo de onde paramos, então precisamos do processo inverso: desserialização . Aqui está o que parecerá em nosso caso:
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

       SavedGame savedGame = (SavedGame) objectInputStream.readObject();

       System.out.println(savedGame);
   }
}
E aqui está o resultado!
SavedGame{territoriesInfo=["Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces], resourcesInfo=[Spain has 100 gold, Russia has 80 gold, France has 90 gold], diplomacyInfo=[France is at war with Russia, Spain has taken a neutral position]}
Excelente! Conseguimos primeiro salvar o estado do nosso jogo em um arquivo e depois restaurá-lo a partir do arquivo. Agora vamos tentar fazer a mesma coisa, mas sem o identificador de versão da nossa SavedGameclasse. Não vamos reescrever nossas duas classes. O código deles permanecerá o mesmo, mas removeremos private static final long serialVersionUIDda SavedGameclasse. Aqui está o nosso objeto após a serialização:
¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;­ТVзй{G xp t pФранция воюет СЃ Россией, Испания заняла позицию нейтралитетаuq ~ t "РЈ Испании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Испании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
Mas veja o que acontece quando tentamos desserializá-lo:
InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
Esta é exatamente a exceção que mencionamos acima. A propósito, perdemos algo importante. Faz sentido que Strings e primitivos possam ser facilmente serializados: Java provavelmente tem algum tipo de mecanismo interno para fazer isso. Mas e se nossa serializableclasse tiver campos que não são primitivos, mas referências a outros objetos? Por exemplo, vamos criar classes TerritoriesInfo, ResourcesInfoe separadas DiplomacyInfopara trabalhar com nossa SavedGameclasse.
public class TerritoriesInfo {

   private String info;

   public TerritoriesInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

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

public class ResourcesInfo {

   private String info;

   public ResourcesInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

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

public class DiplomacyInfo {

   private String info;

   public DiplomacyInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

   @Override
   public String toString() {
       return "DiplomacyInfo{" +
               "info='" + info + '\'' +
               '}';
   }
}
E agora surge uma pergunta: todas essas classes precisam ser Serializablese quisermos serializar nossa SavedGameclasse alterada?
import java.io.Serializable;
import java.util.Arrays;

public class SavedGame implements Serializable {

   private TerritoriesInfo territoriesInfo;
   private ResourcesInfo resourcesInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public TerritoriesInfo getTerritoriesInfo() {
       return territoriesInfo;
   }

   public void setTerritoriesInfo(TerritoriesInfo territoriesInfo) {
       this.territoriesInfo = territoriesInfo;
   }

   public ResourcesInfo getResourcesInfo() {
       return resourcesInfo;
   }

   public void setResourcesInfo(ResourcesInfo resourcesInfo) {
       this.resourcesInfo = resourcesInfo;
   }

   public DiplomacyInfo getDiplomacyInfo() {
       return diplomacyInfo;
   }

   public void setDiplomacyInfo(DiplomacyInfo diplomacyInfo) {
       this.diplomacyInfo = diplomacyInfo;
   }

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoriesInfo=" + territoriesInfo +
               ", resourcesInfo=" + resourcesInfo +
               ", diplomacyInfo=" + diplomacyInfo +
               '}';
   }
}
Bem, vamos testar! Vamos deixar tudo como está e tentar serializar um SavedGameobjeto:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

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

       // Create our object
       TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
       DiplomacyInfo diplomacyInfo =  new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");


       SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

       objectOutputStream.writeObject(savedGame);

       objectOutputStream.close();
   }
}
Resultado:
Exception in thread "main" java.io.NotSerializableException: DiplomacyInfo
Não funcionou! Basicamente, essa é a resposta à nossa pergunta. Quando um objeto é serializado, todos os objetos referenciados por suas variáveis ​​de instância são serializados. E se esses objetos também fizerem referência a outros objetos, eles também serão serializados. E assim por diante ad infinitum. Todas as classes desta cadeia devem serSerializable , caso contrário, será impossível serializá-las e uma exceção será lançada. A propósito, isso pode criar problemas no futuro. O que devemos fazer se, por exemplo, não precisarmos de parte de uma classe quando serializarmos? Ou, por exemplo, e se a TerritoryInfoclasse viesse até nós como parte de alguma biblioteca de terceiros. E suponha ainda que não seja Serializablee, portanto, não podemos mudá-lo. Acontece que não podemos adicionar um TerritoryInfocampo ao nossoSavedGameclass, porque isso tornaria toda a SavedGameclasse não serializável! Isso é um problema :/ Qual é a diferença entre serialização e desserialização em Java?  - 2Em Java, problemas desse tipo são resolvidos usando a transientpalavra-chave. Se você adicionar essa palavra-chave a um campo de sua classe, esse campo não será serializado. Vamos tentar tornar SavedGametransitório um dos campos de instância da classe. Em seguida, serializaremos e restauraremos um objeto.
import java.io.Serializable;

public class SavedGame implements Serializable {

   private transient TerritoriesInfo territoriesInfo;
   private ResourcesInfo resourcesInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   // ...getters, setters, toString()
}



import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

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

       // Create our object
       TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
       DiplomacyInfo diplomacyInfo =  new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");


       SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

       objectOutputStream.writeObject(savedGame);

       objectOutputStream.close();
   }
}


import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

       SavedGame savedGame = (SavedGame) objectInputStream.readObject();

       System.out.println(savedGame);

       objectInputStream.close();


   }
}
E aqui está o resultado:
SavedGame{territoriesInfo=null, resourcesInfo=ResourcesInfo{info='Spain has 100 gold, Russia has 80 gold, France has 90 gold'}, diplomacyInfo=DiplomacyInfo{info='France is at war with Russia, Spain has taken a neutral position'}}
Além disso, obtivemos uma resposta para nossa pergunta sobre qual valor é atribuído a um transientcampo. Ele recebe o valor padrão. Para objetos, isso é null. Você pode ler este excelente artigo sobre serialização quando tiver alguns minutos de sobra. Ele também menciona a Externalizableinterface, sobre a qual falaremos na próxima lição. Além disso, o livro "Head-First Java" tem um capítulo sobre este tópico. Dê-lhe alguma atenção :)
Comentários
  • Populares
  • Novas
  • Antigas
Você precisa acessar para deixar um comentário
Esta página ainda não tem nenhum comentário