Java int, long, short e byte

Números inteiros são os mais usados em qualquer programa: quantidades, idades, contadores, códigos. O Java tem quatro tipos para eles, que diferem só no tamanho. Nesta aula você vai ver quando usar cada um, como escrever números grandes e o que acontece quando um número não cabe no tipo, o famoso overflow.

Você vai aprender
  • Os tipos int, long, short e byte, e por que o int é o padrão.
  • O sufixo L e o erro integer number too large.
  • Os limites MAX_VALUE e MIN_VALUE.
  • O overflow: por que Integer.MAX_VALUE + 1 dá um número negativo, e como se proteger com Math.addExact.
  • Como escrever 1_000_000, números em binário, hexadecimal e octal.

Os quatro tipos inteiros

Pense em potes de tamanhos diferentes. Todos guardam a mesma coisa (números inteiros, positivos ou negativos, sem vírgula); o que muda é até onde cabe.

TipoBitsMenor valorMaior valorUse para
byte8-128127Dados brutos de arquivos e da rede (assunto avançado)
short16-32.76832.767Raramente; economizar memória em grandes volumes
int32-2.147.483.6482.147.483.647Quase tudo: quantidades, idades, contadores
long64-9.223.372.036.854.775.8089.223.372.036.854.775.807Números que podem passar de 2 bilhões

Cada bit a mais dobra a quantidade de valores possíveis. Com 8 bits há 28 = 256 combinações: metade para os negativos (-128 a -1) e o resto para o zero e os positivos (0 a 127). Por isso o maior valor é sempre "um a menos" que o menor, sem o sinal.

int: o padrão

Todo número inteiro que você escreve no código, como 29 ou -5, é um int para o Java. É o tipo mais rápido para o processador e cabe até cerca de 2,1 bilhões, o que basta para a grande maioria das contas:

int alunos = 32;
int temperaturaMinima = -3;
int habitantesDoBrasil = 213000000;

Use int sempre que não houver um bom motivo para outro tipo.


long e o sufixo L

A população mundial passa de 8 bilhões, mais do que cabe num int. É trabalho para o long. Mas veja o que acontece na primeira tentativa:

public class Main {
    public static void main(String[] args) {
        long populacaoMundial = 3000000000;
    }
}
$ javac Main.java
Main.java:3: error: integer number too large
        long populacaoMundial = 3000000000;
                                ^
1 error

"Número inteiro grande demais". Mas a variável é long! O problema não é a variável, é o número escrito: como dito acima, todo número inteiro no código é um int, e 3000000000 não cabe num int. O Java reclama antes mesmo de olhar para a variável.

A solução é dizer que o número é long, colocando um L no fim:

public class Main {
    public static void main(String[] args) {
        long populacaoMundial = 8100000000L;
        long visitas = 25;
        System.out.println(populacaoMundial);
        System.out.println(visitas);
    }
}
$ javac Main.java && java Main
8100000000
25
long
O tipo da variável: inteiro de 64 bits.
populacaoMundial
O nome.
8100000000L
Um literal long ("literal" é um valor escrito direto no código). O L não aparece na saída; só avisa o compilador.

Repare na segunda variável: long visitas = 25; funciona sem L, porque 25 cabe num int e o Java converte sozinho um int para long (um pote menor sempre cabe num maior; veja Conversão de Tipos). O L só é obrigatório quando o número passa de 2.147.483.647.

Use L maiúsculo

O Java também aceita l minúsculo (25l), mas ele parece o dígito 1: 25l lê-se facilmente como "251". Use sempre L.

Missão: a distância da Terra ao Sol é cerca de 149.600.000.000 metros. O código não compila. Corrija para que ele mostre 149600000000.

public class Main {
    public static void main(String[] args) {
        long distanciaAoSol = 149600000000;
        System.out.println(distanciaAoSol);
    }
}
public class Main {
    public static void main(String[] args) {
        long distanciaAoSol = 149600000000L;
        System.out.println(distanciaAoSol);
    }
}

short e byte

São os potes pequenos. Você pode atribuir a eles um número escrito no código desde que ele caiba; o Java confere na compilação:

public class Main {
    public static void main(String[] args) {
        short s = 32767;
        byte b = -128;
        short s2 = 40000;
    }
}
$ javac Main.java
Main.java:5: error: incompatible types: possible lossy conversion from int to short
        short s2 = 40000;
                   ^
1 error

As linhas 3 e 4 passaram: 32.767 e -128 são exatamente os limites. Já 40.000 não cabe num short, e o Java avisa que guardar esse int ali seria uma "conversão com perda". Em programas do dia a dia você quase não vai usar short e byte; o byte aparece quando se lê arquivos e dados da rede, byte a byte.


Os limites: MAX_VALUE e MIN_VALUE

Cada tipo tem duas constantes na sua classe embrulho (wrapper) com o maior e o menor valor:

ConstanteValor
Byte.MIN_VALUE / Byte.MAX_VALUE-128 / 127
Short.MIN_VALUE / Short.MAX_VALUE-32768 / 32767
Integer.MIN_VALUE / Integer.MAX_VALUE-2147483648 / 2147483647
Long.MIN_VALUE / Long.MAX_VALUE-9223372036854775808 / 9223372036854775807

Repare que a classe do int se chama Integer, não Int. Esses valores foram impressos pelo JDK em Tipos Primitivos.


Overflow: quando o número "dá a volta"

O que acontece se você somar 1 ao maior int? Na escrita do código o Java confere os limites, mas durante uma conta ele não confere nada. Veja:

public class Main {
    public static void main(String[] args) {
        int maior = Integer.MAX_VALUE;
        System.out.println(maior);
        System.out.println(maior + 1);
        int menor = Integer.MIN_VALUE;
        System.out.println(menor - 1);
        byte b = 127;
        b++;
        System.out.println(b);
    }
}
$ javac Main.java && java Main
2147483647
-2147483648          « maior + 1 virou o MENOR int
2147483647           « menor - 1 virou o MAIOR int
-128                 « 127 + 1 num byte virou -128

Nenhum erro, nenhum aviso: o programa simplesmente deu a resposta errada. Isso é o overflow ("transbordamento"). Pense no hodômetro de um carro antigo, com 6 rodinhas: depois de 999.999 km, ele volta para 000.000. O int faz o mesmo, só que a "volta" leva do maior positivo ao menor negativo, por causa do jeito como os negativos são guardados em bits.

A linha b++; soma 1 a b (o ++ é explicado em Operadores Aritméticos).

Missão: veja o overflow com seus próprios olhos. No JShell, digite Integer.MAX_VALUE + 1. Depois compare com Integer.MAX_VALUE + 1L (com o L, a conta é feita em long e não transborda).

Integer.MAX_VALUE + 1

O overflow escondido: conta de int guardada em long

Este é o caso que engana até programadores experientes. A Ana quer saber quantos microssegundos há num dia e, prevenida, usa um long:

public class Main {
    public static void main(String[] args) {
        long microsPorDia = 24 * 60 * 60 * 1000 * 1000;
        System.out.println(microsPorDia);
        long certo = 24L * 60 * 60 * 1000 * 1000;
        System.out.println(certo);
    }
}
$ javac Main.java && java Main
500654080            « errado!
86400000000          « certo: 86,4 bilhões

Por que a primeira deu errado se a variável é long? Porque o Java faz a conta primeiro e só depois guarda. Todos os números da conta (24, 60, 1000) são int, então a conta inteira é feita em int, transborda no caminho, e o resultado já errado é guardado no long. Na segunda linha, o 24L faz a conta ser feita em long desde o começo. Essa regra ("o tipo da conta depende dos números envolvidos") se chama promoção e está em Conversão de Tipos.

Como se proteger: Math.addExact

Quando um overflow seria grave (dinheiro, estoque, contagens importantes), o Java oferece métodos que fazem a conta e conferem: Math.addExact (somar), Math.subtractExact (subtrair), Math.multiplyExact (multiplicar). Se o resultado não couber, em vez de dar a volta eles param o programa com um erro:

public class Main {
    public static void main(String[] args) {
        int saldo = 2_000_000_000;
        int deposito = 500_000_000;
        System.out.println(saldo + deposito);
        int total = Math.addExact(saldo, deposito);
        System.out.println(total);
    }
}
$ javac Main.java && java Main
-1794967296
Exception in thread "main" java.lang.ArithmeticException: integer overflow
	at java.base/java.lang.Math.addExact(Math.java:915)
	at Main.main(Main.java:6)
Math.addExact
Método da classe Math que soma dois números com conferência de overflow (exact = "exato").
(saldo, deposito)
Os dois números a somar, separados por vírgula.

A linha 5 mostrou o saldo "negativo" sem reclamar. A linha 6 parou o programa com uma exceção: ArithmeticException: integer overflow ("exceção aritmética: overflow de inteiro"). As linhas que começam com at mostram onde aconteceu: dentro de Math.addExact, chamado na linha 6 do Main.java. O println(total) nunca rodou. Parar com erro parece pior, mas é muito melhor do que seguir com um saldo errado. Exceções têm um capítulo inteiro, começando em O que são Exceções.

SituaçãoO que fazer
Os números nunca chegam perto de 2 bilhõesint e contas normais.
Podem passar de 2 bilhõeslong (e L em pelo menos um número da conta).
Passar do limite seria um erro graveMath.addExact, Math.multiplyExact…
Números sem limite (criptografia, fatorial de 100)BigInteger, irmão do BigDecimal.

Escrevendo números: sublinhado e outras bases

O sublinhado: 1_000_000

Quantos zeros tem 1000000000? Difícil de contar. Desde o Java 7 você pode separar os dígitos com _ (sublinhado), como faria com os pontos em 1.000.000.000. O Java ignora os sublinhados:

public class Main {
    public static void main(String[] args) {
        int milhao = 1_000_000;
        long cartao = 1234_5678_9012_3456L;
        int binario = 0b1010;
        int hexa = 0xFF;
        int octal = 010;
        System.out.println(milhao);
        System.out.println(cartao);
        System.out.println(binario);
        System.out.println(hexa);
        System.out.println(octal);
        System.out.println(Integer.toBinaryString(10));
        System.out.println(Integer.toHexString(255));
    }
}
$ javac Main.java && java Main
1000000
1234567890123456
10
255
8                    « 010 NÃO é dez!
1010
ff

O sublinhado só serve para os seus olhos; na saída ele não aparece. Pode ficar entre dígitos, mas não no começo nem no fim do número, nem colado no L.

Outras bases

Normalmente escrevemos números na base 10 (dígitos de 0 a 9). O Java aceita outras três, indicadas por um prefixo:

PrefixoBaseDígitosExemploValeOnde aparece
(nenhum)10, decimal0–9255255Sempre
0b2, binário0 e 10b101010Bits, permissões, sinais de hardware
0x16, hexadecimal0–9 e A–F0xFF255Cores (0xFF8800), endereços de memória
08, octal0–70108Quase nunca (permissões de arquivo no Linux)

Integer.toBinaryString(10) e Integer.toHexString(255) fazem o caminho inverso: mostram um número em binário e em hexadecimal.

A armadilha do zero à esquerda

Um zero no começo transforma o número em octal: int codigo = 010; guarda oito, não dez. E se aparecer um 8 ou 9 depois do zero, nem compila (veja "illegal digit in an octal literal" abaixo). Nunca escreva zeros à esquerda em números; se o zero importa (CEP, código de produto), o dado é texto: String cep = "09010-000";.


Divisão de inteiros: sem casas decimais

Uma última característica que pega todo iniciante: quando os dois números são inteiros, a divisão também dá um inteiro, e a parte decimal é descartada (não arredondada). 7 / 2 dá 3, e -7 / 2 dá -3. O resto da divisão é obtido com %: 7 % 2 dá 1. Esse assunto tem explicação completa, com exemplos, em Operadores Aritméticos.


Quando dá errado

"integer number too large"
$ javac Main.java
Main.java:3: error: integer number too large
        long populacaoMundial = 3000000000;
                                ^
1 error

O que quer dizer: o número escrito no código passa de 2.147.483.647, o limite de um literal int. Acontece mesmo que a variável seja long.

Como consertar: coloque L no fim: 3000000000L. Se a variável for int, troque também o tipo para long.

"possible lossy conversion from long to int"
public class Main {
    public static void main(String[] args) {
        long a = 10;
        int b = a;
    }
}
$ javac Main.java
Main.java:4: error: incompatible types: possible lossy conversion from long to int
        int b = a;
                ^
1 error

O que quer dizer: o pote maior (long) não cabe automaticamente no menor (int). O Java não olha o valor guardado (10 caberia); olha só os tipos.

Como consertar: se você tem certeza de que o valor cabe, peça a conversão explicitamente: int b = (int) a;. Isso é o casting, explicado em Conversão de Tipos.

"illegal digit in an octal literal"
public class Main {
    public static void main(String[] args) {
        int cep = 09;
    }
}
$ javac Main.java
Main.java:3: error: illegal digit in an octal literal
        int cep = 09;
                   ^
1 error

O que quer dizer: "dígito ilegal num literal octal". O zero à esquerda fez o Java ler o número em base 8, que não tem o dígito 9.

Como consertar: tire o zero (9) ou, se o zero faz parte do dado, use String.

"illegal underscore"
$ javac Main.java
Main.java:3: error: illegal underscore
        int x = 100_;
                   ^
1 error

O que quer dizer: o sublinhado está num lugar proibido: no fim do número. Também é proibido antes do L (100_L) e logo depois de 0x ou 0b. No começo (_100) o erro é outro: o Java acha que é o nome de uma variável e responde cannot find symbol.

Como consertar: sublinhados só entre dígitos: 1_000.

Resultado negativo ou absurdo, sem erro nenhum
$ javac Main.java && java Main
-1794967296

O que aconteceu: overflow. Uma conta passou do limite do tipo e "deu a volta". O javac e o java não avisam.

Como consertar: use long (com L nos números da conta, como 24L * 60) ou Math.addExact/Math.multiplyExact, que lançam ArithmeticException: integer overflow em vez de errar em silêncio.

Missão: o estoque da loja da Ana tem 50.000 caixas com 50.000 parafusos cada. A conta transborda e mostra um número sem sentido. Troque a multiplicação por Math.multiplyExact(caixas, porCaixa) para que o programa pare com ArithmeticException em vez de mostrar o valor errado.

public class Main {
    public static void main(String[] args) {
        int caixas = 50_000;
        int porCaixa = 50_000;
        int parafusos = caixas * porCaixa;
        System.out.println("Parafusos: " + parafusos);
    }
}
public class Main {
    public static void main(String[] args) {
        int caixas = 50_000;
        int porCaixa = 50_000;
        int parafusos = Math.multiplyExact(caixas, porCaixa);
        System.out.println("Parafusos: " + parafusos);
    }
}

Referência

EscritaO que éExemplo
123Literal int (decimal)int n = 123;
123LLiteral longlong n = 8100000000L;
1_000_000Sublinhado para separar dígitosint milhao = 1_000_000;
0b1010Binário (10)int flags = 0b1010;
0xFFHexadecimal (255)int cor = 0xFF8800;
010Octal (8) — evite
Integer.MAX_VALUEMaior int: 2147483647Também Long.MAX_VALUE, Short.MAX_VALUE, Byte.MAX_VALUE
Integer.MIN_VALUEMenor int: -2147483648
Math.addExact(a, b)Soma com conferência de overflowTambém subtractExact, multiplyExact
Integer.toBinaryString(n)Texto com n em binário"1010"
Integer.toHexString(n)Texto com n em hexadecimal"ff"

Exercícios

1. O sufixo

Complete para o código compilar:

long habitantes = 8100000000;

2. O maior int

Complete com a constante que guarda o maior int:

int limite = ;

3. Um milhão legível

Escreva o número um milhão usando sublinhados para separar os milhares:

int premio = ;

4. O que System.out.println(Integer.MAX_VALUE + 1); mostra?

5. Quanto vale int x = 010;?

6. Por que long x = 24 * 60 * 60 * 1000 * 1000; guarda um valor errado?


Resumo

  • Quatro inteiros, do menor ao maior: byte (8 bits), short (16), int (32) e long (64). Use int por padrão e long acima de 2 bilhões.
  • Todo número inteiro escrito no código é int; acima de 2.147.483.647, escreva L no fim, senão: integer number too large.
  • Integer.MAX_VALUE, Integer.MIN_VALUE e companhia guardam os limites.
  • Overflow: passar do limite numa conta não dá erro; o número "dá a volta" (MAX_VALUE + 1 vira MIN_VALUE).
  • Math.addExact/multiplyExact lançam ArithmeticException: integer overflow em vez de errar em silêncio.
  • 1_000_000 é só mais legível. 0b = binário, 0x = hexadecimal, 0 à esquerda = octal (cuidado!).
  • Divisão de inteiros descarta a parte decimal: 7 / 2 é 3.