Desserializando um objeto

Pessoal, estou tendo problemas em desserializar um objeto… Mas especificamente, em acessar seus atributos. Alguem pode me ajudar ?
Data.class:

public class Data implements Serializable {
    private static byte  sequenceNumber[] = new byte[4];
    private static byte ACK[] = new byte[4];
    private static byte connectionID[] = new byte[2];
    private static byte notUsed[] = new byte[2];
    
     public Data(int seq, int ack, int id, int used) {
         sequenceNumber[3] = seq;
         ACK[3] = ack;
         connectionID[1] = id;
         notUsed[1] = used;
     }

    //getters e setters
    ...
}

Cliente.class:

private void converteObjetoEmByte() throws Exception {
    //criando objeto da classe Data
    Data objeto = new Data(123456, 0, 0, 2); 
    byte bytes[] = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream( baos );
    
    oos.writeObject( objeto );
    
    oos.flush();
    oos.close();
    baos.close();
    
    bytes = baos.toByteArray();
    //enviando objeto
    DatagramSocket pacote = new DatagramSocket(
        bytes,
        bytes.length,
        //endereço do servidor
        //porta
    );        
}

Servidor.class

//os bytes do pacote(objeto) foram recebidos 
byte bytesDoPacote[] ;
Data objetoDesserializado;
ByteArrayInputStream bais = new ByteArrayInputStream( bytesDoPacote );
ObjectInputStream ois = new ObjectInputStream( bais );

objetoDesserializado = ois.readObject();

objectIS.close();
byteArrayIS.close();

//O erro ocorre ao acessar os atributos
//Não importa o valor colocado antes da serialização, 
//apos desserializar, o campo fica com 0
objetoDesserializado.getACK() == 0;

Pra resumir, vou explicar oq se passa nos trechos de código acima. Tenho uma classe Data, a qual será serializada.

A classe Cliente q cria um objeto da classe Data, transforma em bytes e envia a um servidor.

A classe Servidor por sua vez, recebe os bytes e transforma o objeto(desserializa) mas ao acessar o atributos do objeto desserializado, eles contem valor nulo mesmo sendo setados antes de serializar. Alguem pode me ajudar a entender o porque ?!

Ola amigo , não seria isto ?

private static final long serialVersionUID = 1L.

Olá @Mrzappabr, você poderia me explicar como o serialVersionUID funciona e o porque do seu uso ?

Ola Iago !

Para simplificar, segue um link, aqui mesmo do GUJ.

Posta os getters da classe Data.

Quando você não declara o serialVersionUID, o compilador cria esta constante em tempo de compilação.
Mas é recomendado declarar ela e ir alterando o valor a medida que a classe tem mais atributos adicionados.

1 curtida

Está documentado na interface Serializable: https://docs.oracle.com/javase/9/docs/api/java/io/Serializable.html

Qual o motivo destes atributos serem arrays de bytes?
Qual o motivo destes atributos serem estáticos?

Vamos lá, estou tentando enviar pacotes de um Cliente para um Servidor local. cada pacote deve conter os seguintes campos:
Sequence Number (32 bits)
Ack Number (32 bits)
Connection ID (16 bits)
Not used (9 bits): Deve ser zero.
A (ACK, 1 bit): Indica que o valor do campo AckNumber é válido.
S (SYN, 1 bit): Sincroniza os números de sequencia, estabelecimento da conexão.
F (FIN, 1 bit): Sem mais dados do emissor, término da conexão.

Estes campos formam o Payload de um pacote, que basicamente contém informações sobre o pacote enviado…

Quanto aos atributos estáticos, eu os defini assim por achar q eles devem pertencer somente aquela classe, apenas.

E sobre os getters, eles retornam um array de bytes, não há nenhum tipo de tratamento, apenas o retorno em bytes[ ]

Tem certeza que é isso mesmo?
A menor unidade de processamento em Java é um byte ou um boolean e ambos tem magnitude de 8 bits.
Mas você tem instruções com 1 bit.

Sim ! Está nas especificações do projeto… O Payload soma 12 bytes de dados fora os dados do que se quer enviar.
Apenas uma correção, Not Used (13 bits): Deve ser zero.*

Agora sim, você postou que not used eram 9 bits e não 13.

Sequence Number (32 bits) ----> utilize uma variável do tipo int

Ack Number (32 bits)      ----> utilize uma variável do tipo int

Connection ID (16 bits)   ----> utilize uma variável do tipo short

Not used (13 bits):       --+-> para estes 4 campos utilize uma única variável do tipo short
                            |
A (ACK, 1 bit):           --+   faça bitmasking para ligar/desligar os 3 primeiros bits (F, S e A)
                            |
S (SYN, 1 bit):           --+
                            |
F (FIN, 1 bit):           --+

Atributos estáticos não são serializados, pois eles pertencem à classe e não ao objeto.

De qualquer forma, nesse seu caso eu não utilizaria a interface Serializable, pois por padrão o método writeObject grava alguns metadados para saber qual classe deve ser instanciada ao fazer o readObject, ou seja, você vai serializar muito mais bytes do que realmente quer.

Eu criaria um método toByteArray na classe Data, para retornar o array de bytes a serem serializados e um construtor que recebe um array de bytes como parâmetro para inicializar os atributos através de um array de bytes recebido.

Muito bom @staroski ! Ao ler as especificações dos atributos, não passou pela minha cabeça o uso dos tipos primitivos int e short e sim o de array de bytes, enfim, muito obrigado, deu uma bela simplificada.
Mais uma pergunta, já que os dados Not used são do tipo short, eu poderia simplesmente atribuir um valor ? Já que eu vou somente mudar os últimos 3 bits, eu teria então valores 1, 2 e 4 para setar. Não conheço o bitmask mas vou procurar sobre.

Enquanto a desserialização, os atributos agora não são mais estáticos, não sabia que dessa forma eles não poderiam ser serializados. E sobre a interface Serializable, a aplicação retornou uma exception onde pedia a implementação da interface. No mais, tudo funcionou, obrigado novamente !

Exemplo:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class Data {

    private static final int ACK_BIT = 2; // posição do bit ACK
    private static final int SYN_BIT = 1; // posição do bit SYN
    private static final int FIN_BIT = 0; // posição do bit FIN

    private int sequenceNumber;
    private int ackNumber;
    private short connectionID;
    private short bitmask;

    /**
     * Cria um objeto Data
     */
    public Data() {}

    /**
     * Cria um objeto Data inicializando os atributos a partir do array informado
     */
    public Data(byte[] bytes) {
        ByteArrayInputStream input = new ByteArrayInputStream(bytes);
        DataInputStream dataReader = new DataInputStream(input);
        try {
            sequenceNumber = dataReader.readInt();
            ackNumber = dataReader.readInt();
            connectionID = dataReader.readShort();
            bitmask = dataReader.readShort();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Converte este objeto Data em um array de bytes
     */
    public byte[] toByteArray() {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        DataOutputStream dataWriter = new DataOutputStream(output);
        try {
            dataWriter.writeInt(sequenceNumber);
            dataWriter.writeInt(ackNumber);
            dataWriter.writeShort(connectionID);
            dataWriter.writeShort(bitmask);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return output.toByteArray();
    }

    /**
     * Obtém a representação textual deste objeto
     */
    @Override
    public String toString() {
        return String.format("%32s%32s%16s%16s",
                             Integer.toBinaryString(sequenceNumber),
                             Integer.toBinaryString(ackNumber),
                             Integer.toBinaryString(connectionID),
                             Integer.toBinaryString(bitmask))
                     .replace(' ', '0');
    }

    public void setSequenceNumber(int sequenceNumber) {
        this.sequenceNumber = sequenceNumber;
    }

    public int getSequenceNumber() {
        return sequenceNumber;
    }

    public void setAckNumber(int ackNumber) {
        this.ackNumber = ackNumber;
    }

    public int getAckNumber() {
        return ackNumber;
    }

    public void setConnectionID(short connectionID) {
        this.connectionID = connectionID;
    }

    public short getConnectionID() {
        return connectionID;
    }

    /**
     * Liga/desliga o bit ACK
     */
    public void setAck(boolean ligado) {
        if (ligado) {
            bitmask |= (1 << ACK_BIT);
        } else {
            bitmask &= ~(1 << ACK_BIT);
        }
    }

    /**
     * Verifica se o bit ACK está ligado
     */
    public boolean isAck() {
        return (bitmask & (1 << ACK_BIT)) == 1;
    }

    /**
     * Liga/desliga o bit SYN
     */
    public void setSyn(boolean ligado) {
        if (ligado) {
            bitmask |= (1 << SYN_BIT);
        } else {
            bitmask &= ~(1 << SYN_BIT);
        }
    }

    /**
     * Verifica se o bit SYN está ligado
     */
    public boolean isSyn() {
        return (bitmask & (1 << SYN_BIT)) == 1;
    }

    /**
     * Liga/desliga o bit FIN
     */
    public void setFin(boolean ligado) {
        if (ligado) {
            bitmask |= (1 << FIN_BIT);
        } else {
            bitmask &= ~(1 << FIN_BIT);
        }
    }

    /**
     * Verifica se o bit FIN está ligado
     */
    public boolean isFin() {
        return (bitmask & (1 << FIN_BIT)) == 1;
    }
}