skip to content

Wrapper Classes & Number Caching

Wrapper mechanics and the valueOf caches — small Integers, Shorts, Bytes, Longs and Characters are shared, so == is true for 127 and false for 128. Interviewers use that puzzle constantly, and follow up with parsing, radix conversion and the bit-manipulation utilities.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What are wrapper classes in Java, and why do they exist?

level: juniorimportance: must knowfreq 70%

answer

  1. 8 primitives, 8 wrappers
  2. Generics/collections need objects
  3. null = absent value
  4. autobox = valueOf, unbox = xxxValue
  5. unboxing null -> NPE

basics

~20 s

Wrapper classes like Integer, Long, Double, Boolean, and Character are object versions of the eight primitive types. They exist because generics and collections only hold objects, not primitives, and they add helper methods and constants.

solid answer

~40 s

Java has eight primitive types (int, long, short, byte, double, float, boolean, char) that are not objects. Each has a matching wrapper class (Integer, Long, Short, Byte, Double, Float, Boolean, Character) that boxes the value inside an object. Wrappers exist because generics and collections (List<Integer>, map keys/values) work only with reference types, so you cannot store a raw int there. Wrappers also carry useful static utilities and constants like Integer.MAX_VALUE, Integer.parseInt, and Integer.compare, and a null wrapper can represent 'no value'. The compiler auto-converts between the two via autoboxing (int to Integer) and unboxing (Integer to int). The trade-offs are extra memory, allocation cost, and a possible NullPointerException when unboxing a null wrapper.

code

java · 9 lines
java
List<Integer> nums = new ArrayList<>();
nums.add(5);              // autobox: Integer.valueOf(5)
int first = nums.get(0);  // unbox: intValue()

Integer maybe = null;
// int boom = maybe;      // NullPointerException on unbox

int max = Integer.MAX_VALUE;          // constant
int parsed = Integer.parseInt("42");  // utility

go deeper

for a junior

Knows the eight wrapper classes, that collections/generics need them, and that autoboxing converts automatically.

for a middle

Explains the null/NPE-on-unbox risk and the memory/allocation cost, and when to prefer primitives.

for a senior

Discusses immutability, valueOf vs new, the caching behavior, and performance impact of boxing in hot loops.

for a principal

Frames wrapper use as an API-design and memory/GC trade-off, advises on primitive specializations (IntStream, primitive arrays) and value-type direction (Project Valhalla) at scale.

## Primitives vs. objects Java values come in two families. **Primitives** are the eight built-in value types: `int`, `long`, `short`, `byte`, `double`, `float`, `boolean`, `char`. They store the value directly (the actual bits) and are fast and small, but they are *not* objects: they have no methods, cannot be `null`, and cannot be used where the language requires an object reference. **Objects** are instances of classes, accessed through a reference (a pointer). Anything declared with a class type is a reference; primitives are a separate category. ## What a wrapper class is A **wrapper class** is a small immutable class that holds (wraps) a single primitive value inside an object. The eight wrappers are: `Integer` (for `int`), `Long`, `Short`, `Byte`, `Double`, `Float`, `Boolean`, and `Character` (for `char`). Once created, a wrapper's value never changes (immutable) — operations produce new wrapper objects. ## Why they exist 1. **Generics and collections only hold references.** `List<int>` is illegal; you must write `List<Integer>`. A `HashMap` key or value must be an object. So to put numbers in a collection, you need the object form. 2. **`null` as 'absent'.** A primitive `int` always has some value (default 0). A wrapper reference can be `null`, which is how you model 'no value yet', e.g. an unset database column. 3. **Utility methods and constants.** Each wrapper carries static helpers: parsing (`Integer.parseInt`), formatting (`Integer.toHexString`), bounds (`Integer.MAX_VALUE`, `Integer.MIN_VALUE`), comparisons (`Integer.compare`), and bit tricks (`Integer.bitCount`). 4. **Polymorphism / Object APIs.** Anything typed as `Object` (e.g. reflection, old non-generic APIs) needs the wrapped form. ## Autoboxing and unboxing Since Java 5 the compiler inserts conversions automatically. **Autoboxing** turns a primitive into its wrapper: `Integer x = 5;` compiles to `Integer x = Integer.valueOf(5);`. **Unboxing** turns a wrapper back into a primitive: `int y = x;` compiles to `int y = x.intValue();`. This makes the two forms feel interchangeable, but the conversion is real machine work and has two important consequences: (a) unboxing a `null` wrapper throws `NullPointerException`, and (b) boxing allocates an object (unless it comes from a cache — see other questions on this leaf). ## Costs A wrapper occupies far more memory than a primitive (an object header plus the field, versus 4 bytes for an `int`), and boxing in a tight loop creates garbage. For performance-sensitive numeric code, prefer primitives and primitive arrays; use wrappers when you need an object (collections, generics, nullability).

  • Name all eight primitive types and their wrapper classes.
    int->Integer, long->Long, short->Short, byte->Byte, double->Double, float->Float, boolean->Boolean, char->Character. Note the two name changes: int->Integer and char->Character.
  • Why can unboxing throw a NullPointerException?
    Because unboxing calls a method on the wrapper (e.g. intValue()). If the wrapper reference is null, that call dereferences null and throws NPE.

A primitive is a loose coin; a wrapper is that coin sealed in a labeled envelope you can mail, file in a folder (collection), or mark 'empty'.

saying these in an interview costs you the question

  • Thinking wrappers are mutable (they are immutable)
  • Claiming List<int> is legal
  • Saying primitives can be null
  • Forgetting that the wrapper names for int and char differ from the primitive (Integer, Character)

context

open as a page

Why does comparing two Integer values with == give true for 127 but false for 128?

level: middleimportance: must knowfreq 85%

basics

~20 s

Integer.valueOf caches the small values -128..127, so autoboxing 127 twice returns the same object and == is true. 128 is outside the cache, so two new objects are created and == compares references, giving false. Always use .equals for wrappers.

open as a page

What is the difference between Integer.parseInt and Integer.valueOf, and how do radix overloads and parsing errors work?

level: middleimportance: should knowfreq 60%

basics

~20 s

parseInt returns a primitive int; valueOf returns an Integer object (possibly from the cache). Both can take a radix (e.g. base 16) and both throw NumberFormatException on bad input. Use parseInt when you want an int, valueOf when you need the wrapper.

open as a page

Why is Integer.compare(a, b) safer than writing a - b, and what is the Math.abs(Integer.MIN_VALUE) quirk?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Integer.compare(a, b) never overflows, but the old trick a - b can overflow when the difference exceeds int range and then return the wrong sign. Similarly Math.abs(Integer.MIN_VALUE) overflows and stays negative, because MIN_VALUE has no positive counterpart.

open as a page

As an architect, how do you reason about autoboxing performance and the integer-cache range across a large, multi-environment Java system?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Avoid boxing in hot paths by using primitives, primitive streams, and primitive-specialized collections; never rely on the integer cache (==) for correctness because its range can differ by JVM config. Enforce these with static analysis and benchmarks rather than intuition.

open as a page