Q5 of 40 · Core Java
Explain Java's primitive types and their wrapper classes.
Short answer
Short answer: Java has 8 primitives (byte, short, int, long, float, double, char, boolean) stored directly on the stack or inline in objects. Each has a wrapper class (Integer, Double, Boolean, etc.) that is a full object, enabling use in collections, generics, and APIs that require objects.
Detail
Java distinguishes between primitive types and reference types. The 8 primitives — byte, short, int, long, float, double, char, boolean — are stored by value. A local int x = 5 lives on the call stack; there is no object header, no GC involvement. This makes primitives fast and memory-efficient.
Wrapper classes (Integer, Long, Double, Boolean, Character, Byte, Short, Float) are regular objects in the heap. They exist because Java's type system requires objects in many places: generic type parameters (List<int> is illegal, List<Integer> works), null values, and utility methods like Integer.parseInt(), Integer.toBinaryString(), Double.isNaN().
Integer cache: the JVM caches Integer objects from -128 to 127. Integer.valueOf(100) == Integer.valueOf(100) is true because they're the same cached instance, but Integer.valueOf(200) == Integer.valueOf(200) is false. This is a well-known interview trap — always use .equals() on wrapper types.
Memory footprint: a primitive int is 4 bytes; an Integer object is ~16 bytes of header + 4 bytes of value. For large arrays of numbers, using int[] instead of Integer[] cuts memory by ~4× and is significantly faster to iterate due to cache locality. In test data builders, prefer primitive arrays for large datasets.
// EXAMPLE
PrimitivesAndWrappers.java
// Primitive vs wrapper
int primitive = 42; // 4 bytes, on stack
Integer wrapped = 42; // ~20 bytes, on heap
// Wrapper required for generics and null
List<Integer> ids = new ArrayList<>();
ids.add(primitive); // autoboxed to Integer automatically
Integer nullable = null; // legal
// int nullable = null; // ❌ compile error
// Integer cache trap — only -128 to 127 are cached
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true (same cached instance)
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false (different heap objects!)
System.out.println(c.equals(d)); // true — always use equals()
// Utility methods live on wrapper classes
int parsed = Integer.parseInt("42");
String binary = Integer.toBinaryString(255); // "11111111"
boolean isNaN = Double.isNaN(0.0 / 0.0); // true