O que significa Serializable?

136

O que exatamente significa para uma classe estar Serializableem Java? Ou, em geral, para esse assunto ...

Ritwik Bose
fonte
10
@skaffman Aqui está o que diz para a classe Serializable: Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.
Ritwik Bose
32
Uma ótima explicação se você já sabe o que significa serializado e desserializado. (Não é um elogio.) Essas definições ajudam a entender melhor o problema tecnicamente uma vez e apenas uma vez, você já tem algum conhecimento sobre ele.
Xonatron

Respostas:

132

A serialização está persistindo um objeto da memória para uma sequência de bits, por exemplo, para salvar no disco. A desserialização é o oposto - ler dados do disco para hidratar / criar um objeto.

No contexto da sua pergunta, é uma interface que, se implementada em uma classe, essa classe pode ser serializada e desserializada automaticamente por diferentes serializadores.

Oded
fonte
2
Observe também que todos os campos não marcados explicitamente também serão serializados. Isso significa que você pode salvar facilmente uma estrutura de dados complexa apenas serializando o objeto raiz.
Thorbjørn Ravn Andersen
1
Então, quando falamos de "Objetos", queremos dizer o objeto instanciado por uma classe ou apenas qualquer "Objeto de Software", como assemblies, arquivos, etc.? E, se for o último, é apenas uma maneira padronizada de enviar dados entre programas e ambientes?
Sunburst275
1
@ Sunburst275 - neste caso, é a representação na memória de uma classe na memória - ou seja, uma instância de uma classe (não há um ponto real em falar sobre serialização de assemblies, pois eles geralmente estão no disco como arquivos que podem basta ser enviado como está).
Oded
44

Embora a maioria dos usuários já tenha dado a resposta, gostaria de adicionar um exemplo para aqueles que precisam, a fim de explicar a idéia:

Digamos que você tenha uma pessoa da classe como a seguinte:

public class Person implements java.io.Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public String firstName;
    public String lastName;
    public int age;
    public String address;

    public void play() {
        System.out.println(String.format(
                "If I win, send me the trophy to this address: %s", address));
    }
    @Override
    public String toString() {
        return String.format(".....Person......\nFirst Name = %s\nLast Name = %s", firstName, lastName);
    }
}

e então você cria um objeto como este:

Person william = new Person();
        william.firstName = "William";
        william.lastName = "Kinaan";
        william.age = 26;
        william.address = "Lisbon, Portugal";

Você pode serializar esse objeto para vários fluxos. Farei isso em dois fluxos:

Serialização para saída padrão:

public static void serializeToStandardOutput(Person person)
            throws IOException {
        OutputStream outStream = System.out;
        ObjectOutputStream stdObjectOut = new ObjectOutputStream(outStream);
        stdObjectOut.writeObject(person);
        stdObjectOut.close();
        outStream.close();
    }

Serialização para um arquivo:

public static void serializeToFile(Person person) throws IOException {
        OutputStream outStream = new FileOutputStream("person.ser");
        ObjectOutputStream fileObjectOut = new ObjectOutputStream(outStream);
        fileObjectOut.writeObject(person);
        fileObjectOut.close();
        outStream.close();
    }

Então:

Desserializar do arquivo:

public static void deserializeFromFile() throws IOException,
            ClassNotFoundException {
        InputStream inStream = new FileInputStream("person.ser");
        ObjectInputStream fileObjectIn = new ObjectInputStream(inStream);
        Person person = (Person) fileObjectIn.readObject();
        System.out.println(person);
        fileObjectIn.close();
        inStream.close();
    }
William Kinaan
fonte
Obrigado. Eu acho que entendi agora.
Sunburst275
40

Isso significa que as instâncias da classe podem ser transformadas em um fluxo de bytes (por exemplo, para serem salvas em um arquivo) e depois convertidas novamente em classes novamente. Esse recarregamento pode ocorrer em uma instância diferente do programa ou mesmo em uma máquina diferente. A serialização (em qualquer idioma) envolve todos os tipos de problemas, especialmente quando você tem referências a outros objetos dentro do serializável.

David
fonte
15

Aqui está uma explicação detalhada da serialização : (meu próprio blog)

Serialização:

Serialização é o processo de serializar o estado de um objeto que é representado e armazenado na forma de uma sequência de bytes. Isso pode ser armazenado em um arquivo. O processo para ler o estado do objeto no arquivo e restaurá-lo é chamado desserialização.

Qual é a necessidade de serialização?

Na arquitetura moderna, sempre há a necessidade de armazenar o estado do objeto e depois recuperá-lo. Por exemplo, no Hibernate, para armazenar um objeto, devemos tornar a classe Serializable. O que ele faz é que, uma vez que o estado do objeto é salvo na forma de bytes, ele pode ser transferido para outro sistema que pode ler o estado e recuperar a classe. O estado do objeto pode vir de um banco de dados ou de uma jvm diferente ou de um componente separado. Com a ajuda da serialização, podemos recuperar o estado do objeto.

Código Exemplo e explicação:

Primeiro, vamos dar uma olhada na classe Item:

public class Item implements Serializable{

    /**
    *  This is the Serializable class
    */
    private static final long serialVersionUID = 475918891428093041L;
    private Long itemId;
    private String itemName;
    private transient Double itemCostPrice;
    public Item(Long itemId, String itemName, Double itemCostPrice) {
        super();
        this.itemId = itemId;
        this.itemName = itemName;
        this.itemCostPrice = itemCostPrice;
      }

      public Long getItemId() {
          return itemId;
      }

     @Override
      public String toString() {
          return "Item [itemId=" + itemId + ", itemName=" + itemName + ", itemCostPrice=" + itemCostPrice + "]";
       }


       public void setItemId(Long itemId) {
           this.itemId = itemId;
       }

       public String getItemName() {
           return itemName;
       }
       public void setItemName(String itemName) {
            this.itemName = itemName;
        }

       public Double getItemCostPrice() {
            return itemCostPrice;
        }

        public void setItemCostPrice(Double itemCostPrice) {
             this.itemCostPrice = itemCostPrice;
        }
}

No código acima, pode ser visto que a classe Item implementa Serializable .

Essa é a interface que permite que uma classe seja serializável.

Agora podemos ver que uma variável chamada serialVersionUID é inicializada na variável Long. Esse número é calculado pelo compilador com base no estado da classe e nos atributos da classe. Este é o número que ajudará a jvm a identificar o estado de um objeto ao ler o estado do objeto no arquivo.

Para isso, podemos dar uma olhada na documentação oficial da Oracle:

O tempo de execução de serialização associa a cada classe serializável um número de versão, chamado serialVersionUID, usado durante a desserialização para verificar se o remetente e o destinatário de um objeto serializado carregaram classes para esse objeto que são compatíveis com a serialização. Se o receptor carregou uma classe para o objeto que possui um serialVersionUID diferente daquele da classe do remetente correspondente, a desserialização resultará em uma InvalidClassException. Uma classe serializável pode declarar seu próprio serialVersionUID explicitamente declarando um campo chamado "serialVersionUID" que deve ser estático, final e do tipo long: ANY-ACCESS-MODIFIER estático final long serial serialVersionUID = 42L; Se uma classe serializável não declarar explicitamente um serialVersionUID, o tempo de execução da serialização calculará um valor serialVersionUID padrão para essa classe com base em vários aspectos da classe, conforme descrito na Especificação de serialização de objetos Java (TM). No entanto, é altamente recomendável que todas as classes serializáveis ​​declarem explicitamente os valores serialVersionUID, pois a computação serialVersionUID padrão é altamente sensível aos detalhes da classe que podem variar dependendo das implementações do compilador e, portanto, resultar em InvalidClassExceptions inesperadas durante a desserialização. Portanto, para garantir um valor consistente serialVersionUID em diferentes implementações do compilador java, uma classe serializável deve declarar um valor explícito serialVersionUID. Também é altamente recomendável que declarações explícitas serialVersionUID usem o modificador privado sempre que possível,

Se você notou que existe outra palavra-chave que é transitória .

Se um campo não for serializável, ele deve ser marcado como transitório. Aqui, marcamos o itemCostPrice como transitório e não queremos que ele seja gravado em um arquivo

Agora vamos dar uma olhada em como escrever o estado de um objeto no arquivo e depois ler a partir daí.

public class SerializationExample {

    public static void main(String[] args){
        serialize();
       deserialize();
    } 

    public static void serialize(){

         Item item = new Item(1L,"Pen", 12.55);
         System.out.println("Before Serialization" + item);

         FileOutputStream fileOut;
         try {
             fileOut = new FileOutputStream("/tmp/item.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(item);
             out.close();
             fileOut.close();
             System.out.println("Serialized data is saved in /tmp/item.ser");
           } catch (FileNotFoundException e) {

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

                  e.printStackTrace();
           }
      }

    public static void deserialize(){
        Item item;

        try {
                FileInputStream fileIn = new FileInputStream("/tmp/item.ser");
                ObjectInputStream in = new ObjectInputStream(fileIn);
                item = (Item) in.readObject();
                System.out.println("Serialized data is read from /tmp/item.ser");
                System.out.println("After Deserialization" + item);
        } catch (FileNotFoundException e) {
                e.printStackTrace();
        } catch (IOException e) {
               e.printStackTrace();
        } catch (ClassNotFoundException e) {
               e.printStackTrace();
        }
     }
}

No exemplo acima, podemos ver um exemplo de serialização e desserialização de um objeto.

Para isso, usamos duas classes. Para serializar o objeto, usamos ObjectOutputStream. Nós usamos o método writeObject para gravar o objeto no arquivo.

Para desserializar, usamos ObjectInputStream que lê o objeto no arquivo. Ele usa readObject para ler os dados do objeto no arquivo.

A saída do código acima seria como:

Before SerializationItem [itemId=1, itemName=Pen, itemCostPrice=12.55]
Serialized data is saved in /tmp/item.ser
After DeserializationItem [itemId=1, itemName=Pen, itemCostPrice=null]

Observe que itemCostPrice do objeto desserializado é nulo, pois não foi gravado.

Pritam Banerjee
fonte
2
Obrigado por esta explicação detalhada.
Promise Preston
Esta é uma boa explicação! Obrigado! Mas, para ser sincero, essa entrada parece muito mais limpa do que a do seu blog. Enfim, isso ajudou muito!
Sunburst275
11

A serialização envolve salvar o estado atual de um objeto em um fluxo e restaurar um objeto equivalente desse fluxo. O fluxo funciona como um contêiner para o objeto

Jigar Joshi
fonte
1
Essa definição parece mais precisa. Obrigado.
Promise Preston
6

Serializable é chamado como uma interface, mas é mais como um sinalizador para o subsistema Serialization, em tempo de execução. Diz que este objeto pode ser salvo. Todas as variáveis ​​de instância de objetos, com exceção de nenhum objeto serializável e aquelas marcadas como voláteis, serão salvas.

Imagine que seu aplicativo possa alterar a cor como uma opção, sem manter essa configuração externa, você precisará alterar a cor toda vez que a executar.

AphexMunky
fonte
5
Não é um 'sinalizador para o compilador'. É um sinalizador para o subsistema de serialização, em tempo de execução.
Marquês de Lorne
1
@EJP - Obrigado, não sabia que
AphexMunky
Com respeito, por que escrevê-lo quando você não sabe que é verdade? Você também deixou de fora 'transitório'. Em suma, uma resposta ruim, desculpe.
Marquês de Lorne
19
Se eu não tivesse escrito, não teria sido corrigido e ficaria pior. Todas as outras respostas também foram transitórias. Você nem escreveu uma resposta, está apenas perseguindo outras pessoas.
AphexMunky
4

Serialização é uma técnica para armazenar ou gravar objetos e dados em arquivos. Usando ObjectOutputStreame FileOutputStreamclasses. Essas classes têm seus métodos específicos para persistir os objetos. gostarwriteObject();

para uma explicação clara com figuras. Veja aqui para mais informações

Mdhar9e
fonte
2

Apresentar de outra perspectiva. A serialização é um tipo de interface chamada 'interface do marcador'. Uma interface de marcador é uma interface que não contém declarações de método, mas apenas designa (ou "marca") uma classe que implementa a interface como tendo alguma propriedade. Se você entender o polimorfismo, isso fará muito sentido. No caso da interface do marcador Serializable, o método ObjectOutputStream.write (Object) falhará se o argumento não implementar a interface. Este é um erro potencial em java, poderia ter sido ObjectOutputStream.write (Serializable)

Altamente recomendado: Leia o item 37 do Effective Java de Joshua Bloch para saber mais.

nanospeck
fonte
2

Serialização: Gravando o estado do objeto em um arquivo / rede ou em qualquer outro lugar. (Formulário médio suportado por objeto Java para formulário suportado por arquivo ou formulário suportado por rede)

Desserialização: Leitura do estado do objeto em Arquivo / Rede ou em qualquer outro lugar. (Formulário médio suportado por arquivo / rede para o formulário suportado por objeto Java)

Asif Mushtaq
fonte
1

Apenas para adicionar às outras respostas e com relação à generalidade. A serialização às vezes é conhecida como arquivamento, por exemplo, em Objective-C.

Greg Sexton
fonte