Java – Tipos de Datos

Java tiene ocho tipos primitivos y todo lo demás son objetos. La diferencia importa más de lo que parece: un primitivo guarda el valor, un objeto guarda una referencia, y un primitivo nunca puede ser null.

Los ocho primitivos

TipoTamañoRangoPor omisión
byte1 byte−128 .. 1270
short2 bytes−32.768 .. 32.7670
int4 bytes−2.147.483.648 .. 2.147.483.6470
long8 bytes−9.223.372.036.854.775.808 .. 9.223.372.036.854.775.8070L
float4 bytes±3,4 × 1038, con unos 7 dígitos buenos0.0f
double8 bytes±1,8 × 10308, con unos 15 dígitos buenos0.0d
booleansin definirtrue o falsefalse
char2 bytes0 .. 65.535 (una unidad UTF-16)'\u0000'

Los rangos no hay que aprendérselos: están en las constantes de las clases envoltorio, y así es como conviene consultarlos.

System.out.println("int    " + Integer.MIN_VALUE + " .. " + Integer.MAX_VALUE);
System.out.println("long   " + Long.MIN_VALUE + " .. " + Long.MAX_VALUE);
System.out.println("double " + Double.MIN_VALUE + " .. " + Double.MAX_VALUE);
salidaint    -2147483648 .. 2147483647
long   -9223372036854775808 .. 9223372036854775807
double 4.9E-324 .. 1.7976931348623157E308
Ojo con MIN_VALUE: en los enteros es el más negativo, pero en float y double es el positivo más pequeño que se puede representar, no el más negativo. El más negativo es -Double.MAX_VALUE. Es una asimetría que despista a todo el mundo la primera vez.

Cómo se escriben

byte   b = 10;
short  s = 266;
int    i = 1231232;
long   l = 10_000_000_000L;      // la L final es obligatoria si no cabe en un int
float  f = 4.15f;                // la f final es obligatoria
double d = 12312.2;              // por omisión, un decimal es double
boolean existe = true;
char   c = 'a';
char   salto = '\n';
javac SinL.javaSinL.java:3: error: integer number too large
        long l = 10000000000;
                 ^
1 error

Los guiones bajos de 10_000_000_000L no son decorativos ni cambian el valor: el compilador los ignora y están para que un número largo se lea. Y sin la L, ese literal no compila, porque por omisión se interpreta como int y no cabe; el error de arriba es el que da javac.

El valor por omisión existe, pero no siempre

public class Tipos {
    static int campoSinValor;
    static boolean banderaSinValor;
    static String textoSinValor;

    public static void main(String[] args) {
        System.out.println("int sin valor     = " + campoSinValor);
        System.out.println("boolean sin valor = " + banderaSinValor);
        System.out.println("String sin valor  = " + textoSinValor);
    }
}
salidaint sin valor     = 0
boolean sin valor = false
String sin valor  = null
Eso vale para los campos de una clase. Una variable local dentro de un método no tiene valor por omisión: si la usas sin asignarle nada, el programa no compila. El compilador te dice variable x might not have been initialized, y hace bien.

Dos cosas que sorprenden

int grande = Integer.MAX_VALUE;
System.out.println("MAX_VALUE + 1 = " + (grande + 1));
System.out.println("0.1 + 0.2 = " + (0.1 + 0.2));
salidaMAX_VALUE + 1 = -2147483648
0.1 + 0.2 = 0.30000000000000004

La primera es el desbordamiento: al pasarse del máximo, un int da la vuelta y aparece en el mínimo. Java no avisa. Si el número puede crecer, usa long, y si tiene que ser exacto y grande, BigInteger.

La segunda es que double y float son binarios: 0,1 y 0,2 no tienen representación exacta en base dos, igual que 1/3 no la tiene en base diez. Para dinero no se usan nunca; para eso está BigDecimal.

char no es un carácter, es media cosa

String emoji = "\uD83D\uDE80";                                  // un cohete
System.out.println("length() = " + emoji.length());
System.out.println("codePointCount = " + emoji.codePointCount(0, emoji.length()));
salidalength() = 2
codePointCount = 1

Un char guarda una unidad de UTF-16, que es suficiente para las letras latinas, los acentos y la eñe, pero no para los emojis ni para buena parte del chino y el japonés: esos ocupan dos. Por eso length() devuelve 2 para un solo símbolo. Si vas a contar caracteres de verdad, codePointCount.

Los no primitivos

Todo lo demás —String, los arreglos, las listas, tus propias clases— son objetos. Se distinguen en tres cosas: empiezan por mayúscula, pueden valer null y tienen métodos.

String cad = "Esto es una cadena";
System.out.println(cad.toUpperCase());
System.out.println(cad.length());

String es además inmutable: toUpperCase() no cambia la cadena, devuelve otra. Y por eso dos cadenas se comparan con equals, nunca con ==, que compara si son el mismo objeto en memoria.

Desde Java 10 puedes escribir var y dejar que el compilador deduzca el tipo: var i = 10; es un int y var cad = "hola"; es un String. No es tipado dinámico —el tipo se fija al compilar y no cambia—, solo te ahorra escribirlo.

Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.