Basics

1. Primitive Types and Their Wrapper Classes

Java has 8 primitive types; each has a corresponding wrapper class that enables use in generics and collections, but autoboxing introduces subtle pitfalls.

Java 21 (core language)

Explanation

Java's 8 primitive types are: boolean, byte, short, int, long, float, double, and char. Each has a corresponding wrapper class in java.lang: Boolean, Byte, Short, Integer, Long, Float, Double, and Character.

Autoboxing (primitive โ†’ wrapper) and unboxing (wrapper โ†’ primitive) happen automatically since Java 5. However, they hide real costs: every boxed value is a heap allocation, and unnecessary boxing in tight loops can cause significant GC pressure.

The most dangerous unboxing trap is a NullPointerException when unboxing a null wrapper: Integer i = null; int x = i; throws at runtime because Java tries to call i.intValue(). Always check for null before unboxing from a collection or method return.

Use primitives wherever possible for performance-sensitive code. Use wrappers only when required by generics (List<Integer>), when you need null semantics, or when using utility methods like Integer.parseInt().

Example

import java.util.ArrayList;
import java.util.List;

public class PrimitiveVsWrapper {
    public static void main(String[] args) {
        // Autoboxing: int -> Integer (heap allocation)
        Integer boxed = 42;
        // Unboxing: Integer -> int
        int primitive = boxed;

        // NullPointerException trap
        Integer nullable = null;
        try {
            int danger = nullable; // unboxing null -> NPE!
        } catch (NullPointerException e) {
            System.out.println("Unboxing null threw NPE");
        }

        // Generics require wrapper types
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1); // autoboxed
        numbers.add(2);

        // Performance: prefer int[] over Integer[] for large arrays
        int[] primitiveArray   = new int[1_000_000];   // ~4 MB
        Integer[] wrapperArray = new Integer[1_000_000]; // ~16 MB+ (pointers + objects)

        System.out.println("Boxed:     " + boxed);
        System.out.println("Primitive: " + primitive);
        System.out.println("Sum: " + numbers.stream().mapToInt(Integer::intValue).sum());
    }
}

Sources