HOWTO · Java

Converter Byte Array em Integer em Java

Este tutorial demonstra como converter array de bytes em int em Java

Nesta página

Converter um array de bytes em um inteiro em Java é uma tarefa comum ao lidar com protocolos de rede, E/S de arquivos ou formatos de dados binários. A abordagem correta depende do que os bytes representam: às vezes vários bytes codificam um único int, e às vezes cada byte é um valor independente. Este artigo cobre os métodos do núcleo do Java — ByteBuffer, DataInputStream, deslocamento manual de bits e BigInteger — além das regras de ordem de bytes e de sinal que determinam se o resultado está correto.

Converter quatro bytes em um int com ByteBuffer

O ByteBuffer (em java.nio) é a ferramenta mais direta do núcleo do Java para essa conversão. O método wrap cria um buffer que compartilha o armazenamento do array, e getInt() lê quatro bytes a partir da posição atual do buffer e os interpreta como um único int. Execute este exemplo:

Os rótulos exibidos pelos exemplos de código e saída desta seção permanecem em inglês.

import java.nio.ByteBuffer;

public class ByteArrayToInt {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int intValue = ByteBuffer.wrap(byteArray).getInt();

    System.out.println("Converted Integer Value: " + intValue);
  }
}

Saída:

Converted Integer Value: 66320

Um ByteBuffer é big-endian por padrão em qualquer plataforma: o primeiro byte do array é tratado como o byte mais significativo. Se os dados foram gravados em ordem little-endian, selecione essa ordem explicitamente com ByteOrder antes de ler:

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ByteArrayToIntOrder {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int bigEndianValue = ByteBuffer.wrap(byteArray).getInt();
    int littleEndianValue =
        ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN).getInt();

    System.out.println("big-endian: " + bigEndianValue);
    System.out.println("little-endian: " + littleEndianValue);
  }
}

Saída:

big-endian: 66320
little-endian: 268632320

Dois detalhes são importantes aqui. Primeiro, getInt() lê a partir da posição atual do buffer e a avança em quatro posições, portanto chamá-lo duas vezes no mesmo buffer lê os quatro bytes seguintes; um novo wrap sempre começa na posição 0. Segundo, quando restam menos de quatro bytes, getInt() lança um BufferUnderflowException, então verifique o comprimento da entrada antes de ler.

Ler quatro bytes com DataInputStream

Quando os bytes chegam por meio de um stream — ou quando você precisa ler vários valores primitivos em sequência — o DataInputStream é uma boa opção. Encapsule o array em um ByteArrayInputStream, encapsule esse em um DataInputStream e chame readInt():

Os rótulos exibidos pelos exemplos de código e saída desta seção permanecem em inglês.

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

public class ByteArrayToIntWithDataInputStream {
  public static void main(String[] args) throws IOException {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    try (DataInputStream input =
        new DataInputStream(new ByteArrayInputStream(byteArray))) {
      int intValue = input.readInt();
      System.out.println("Converted Integer Value: " + intValue);
    }
  }
}

Saída:

Converted Integer Value: 66320

O readInt() sempre interpreta os próximos quatro bytes em ordem big-endian, independentemente da plataforma. Quando restam menos de quatro bytes no stream, ele lança um EOFException, que você pode capturar para relatar uma entrada curta em vez de deixar o programa falhar:

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;

public class ByteArrayToIntShortInput {
  public static void main(String[] args) throws IOException {
    byte[] byteArray = {0x03, 0x10};

    try (DataInputStream input =
        new DataInputStream(new ByteArrayInputStream(byteArray))) {
      int intValue = input.readInt();
      System.out.println("Converted Integer Value: " + intValue);
    } catch (EOFException e) {
      System.out.println("readInt() needs four bytes, but only " + byteArray.length + " remain");
    }
  }
}

Saída:

readInt() needs four bytes, but only 2 remain

Construir um int deslocando bytes

Deslocar os bytes manualmente mostra exatamente como a conversão funciona e dá controle total sobre a ordem e o comprimento. Para cada byte, desloque o valor acumulado 8 bits à esquerda e combine o próximo byte com OR:

Os rótulos exibidos pelos exemplos de código e saída desta seção permanecem em inglês.

public class ByteArrayToIntManually {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int intValue = 0;

    for (byte b : byteArray) {
      intValue = (intValue << 8) | (b & 0xFF);
    }

    System.out.println("Converted Integer Value: " + intValue);
  }
}

Saída:

Converted Integer Value: 66320

A máscara b & 0xFF é necessária porque o tipo byte do Java tem sinal: 0x80 é armazenado como -128. Sem a máscara, | combinaria 0xFFFFFF80 em vez de 0x00000080, corrompendo todos os bytes mais altos. Este laço monta os bytes em ordem big-endian — o primeiro elemento do array é o byte mais significativo — e pressupõe que o array contém exatamente quatro bytes. Para uma ordem diferente, percorra o array na direção oposta; para mais bytes, acumule em um long em vez de um int.

Interpretar um array de bytes com BigInteger

O BigInteger aceita um array de bytes diretamente em seu construtor e o interpreta como um número com sinal, big-endian, em complemento de dois, de comprimento arbitrário. O intValue() então reduz o resultado para um int:

Os rótulos exibidos pelos exemplos de código e saída desta seção permanecem em inglês.

import java.math.BigInteger;

public class ByteArrayToIntWithBigInteger {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int intValue = new BigInteger(byteArray).intValue();

    System.out.println("Converted Integer Value: " + intValue);
  }
}

Saída:

Converted Integer Value: 66320

Como a interpretação tem sinal, um bit inicial 1 produz um valor abaixo de 0; passe um signum de 1 como primeiro argumento do construtor para obter a interpretação sem sinal. Lembre-se de que intValue() mantém apenas os 32 bits mais baixos — ele trunca silenciosamente os valores que não cabem em um int:

import java.math.BigInteger;

public class BigIntegerSignAndRange {
  public static void main(String[] args) {
    byte[] allOnes = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};

    System.out.println("signed: " + new BigInteger(allOnes));
    System.out.println("unsigned: " + new BigInteger(1, allOnes));
    System.out.println("narrowed to int: " + new BigInteger(1, allOnes).intValue());
  }
}

Saída:

signed: -1
unsigned: 4294967295
narrowed to int: -1

O BigInteger é a escolha natural quando o array tem mais de quatro bytes ou quando você precisa da magnitude sem sinal antes da redução.

Converter cada byte separadamente

Todos os métodos acima combinam vários bytes em um único número. Se cada byte é um valor independente — leituras de sensores, canais de cor, campos de protocolo — converta os elementos individualmente. Observe a distinção: getShort() ou readShort() sobre dois bytes produz um único short combinado; isso não converte cada byte separadamente.

Lembre-se também de que o tipo byte do Java tem sinal e varia de -128 a 127. Quando a intenção é um valor sem sinal de 0 a 255, use Byte.toUnsignedInt (disponível desde o Java 8) ou a máscara equivalente b & 0xFF:

Os rótulos exibidos pelos exemplos de código e saída desta seção permanecem em inglês.

public class PrintEachByte {
  public static void main(String[] args) {
    byte[] byteArray = {0x03, 0x04, (byte) 0x80, (byte) 0xFF};

    for (byte b : byteArray) {
      System.out.println("signed: " + b + " unsigned: " + Byte.toUnsignedInt(b));
    }
  }
}

Saída:

signed: 3 unsigned: 3
signed: 4 unsigned: 4
signed: -128 unsigned: 128
signed: -1 unsigned: 255

Escolher um método e tratar casos extremos

Cada abordagem se encaixa em uma situação diferente:

  • ByteBuffer é a primeira escolha para ler um int de quatro bytes de um array: é compacto, suporta ambas as ordens de bytes por meio de ByteOrder e fornece getLong(), getShort() e afins para outros tamanhos.
  • DataInputStream serve para código que já trabalha com streams e precisa ler vários valores tipados em sequência; suas leituras são sempre big-endian.
  • O deslocamento manual custa algumas linhas a mais, mas controla a ordem, a posição inicial e a largura com precisão e não precisa de nenhum objeto de buffer ou de stream.
  • BigInteger lida com arrays maiores que quatro bytes e com a interpretação explícita com ou sem sinal antes da redução.
  • A conversão byte a byte com Byte.toUnsignedInt se aplica quando cada elemento é um valor próprio, e não parte de um único número.

Qualquer que seja o método escolhido, verifique estes pontos antes de converter:

  • Comprimento da entrada: uma leitura de quatro bytes em um array mais curto falha — BufferUnderflowException para ByteBuffer, EOFException para DataInputStream, e um ArrayIndexOutOfBoundsException ou um resultado silenciosamente errado para o código manual.
  • Ordem dos bytes: a ordem do leitor deve corresponder à ordem em que os bytes foram gravados; ByteBuffer usa big-endian por padrão e pode ser trocado com ByteOrder, enquanto as leituras de DataInputStream são sempre big-endian, sem opção de ordem.
  • Sinal e faixa: getInt() e readInt() retornam um int com sinal, portanto um valor de 32 bits sem sinal acima de Integer.MAX_VALUE aparece abaixo de 0. Use Integer.toUnsignedLong(intValue) ou acumule em um long quando os dados de origem forem sem sinal.
  • Posição do buffer: reutilizar um ByteBuffer continua a partir de sua posição atual, não do início; chame rewind() ou wrap novamente para reler o array.