Sistema de tipos

Primitivos, referencias, autoboxing y null

Tipos primitivos

Los ocho primitivos son los únicos tipos que no son objetos en Java. Se almacenan directamente en la pila (o en el heap dentro de un objeto), sin overhead de cabecera de objeto. Son siempre de tamaño fijo, independientemente de la plataforma.

// Ocho tipos primitivos en Java
byte   b = 127;          // 8 bits,  -128..127
short  s = 32767;        // 16 bits
int    i = 2_147_483_647;// 32 bits (literal con guiones bajos para legibilidad)
long   l = 9_000_000_000L; // 64 bits, sufijo L obligatorio
float  f = 3.14f;        // 32 bits IEEE 754, sufijo f
double d = 3.14159265;   // 64 bits IEEE 754
boolean flag = true;
char   c = 'A';          // 16 bits, Unicode UTF-16

Tipos referencia y null

Todo lo demás es un tipo referencia: clases, interfaces, arrays. Una variable de tipo referencia almacena la dirección del objeto en el heap, no el objeto en sí. null indica ausencia de referencia — acceder a cualquier método o campo de una referencia nula lanza NullPointerException.

// Los tipos referencia almacenan una dirección, no el valor
String s1 = "hola";
String s2 = s1;     // s2 apunta al mismo objeto

// null: ausencia de referencia
String s3 = null;   // s3 no apunta a ningún objeto
s3.length();        // NullPointerException

// Comparación: == compara referencias, no contenido
String a = new String("hola");
String b = new String("hola");
a == b;       // false: objetos distintos en memoria
a.equals(b);  // true: mismo contenido

Autoboxing y unboxing

Java convierte automáticamente entre primitivos y sus tipos envolventes (wrappers). Esto permite usar primitivos donde se espera un Object o en colecciones genéricas. El compilador inserta las llamadas a valueOf() y intValue() silenciosamente.

// Autoboxing: primitivo → objeto envolvente (wrapper)
int primitivo = 42;
Integer objeto = primitivo;          // autoboxing automático
Integer otro   = Integer.valueOf(42); // equivalente explícito

// Unboxing: objeto → primitivo
int desempaquetado = objeto;          // unboxing automático

// Wrappers disponibles:
// int → Integer, long → Long, double → Double, boolean → Boolean
// char → Character, byte → Byte, short → Short, float → Float

// TRAMPA: unboxing de null lanza NullPointerException
Integer nulo = null;
int valor = nulo; // NullPointerException

Cache de wrappers y rendimiento

// Cache de Integer: -128..127 reutilizan la misma instancia
Integer a = 100;
Integer b = 100;
a == b; // true  — misma instancia cacheada

Integer c = 200;
Integer d = 200;
c == d; // false — instancias distintas (fuera del rango de cache)

// Moraleja: nunca comparar wrappers con ==, usar equals()
c.equals(d); // true — correcto

// Coste de autoboxing en bucles intensivos
long suma = 0;
// LENTO: crea un Long en cada iteración del bucle
for (Long i = 0L; i < 1_000_000; i++) suma += i; // evitar

// RÁPIDO: todo en primitivos
long suma2 = 0;
for (long i = 0; i < 1_000_000; i++) suma2 += i;
Regla práctica: usa primitivos (int, long...) en lógica de negocio y bucles. Usa wrappers (Integer, Long...) cuando necesitas almacenar en colecciones genéricas o cuando el valor puede ser null. Nunca compares wrappers con == — usa equals().

Siguiente apartado → Encapsulamiento de estado: getters, setters y el método main

Índice de la sección

Índice del curso