Alexander Claes
← Learning Java

Java wrapper classes: boxing and unboxing explained

Why does Java have both int and Integer? Learn how wrapper classes work, what boxing and unboxing mean, and when to use primitives or objects—with simple examples.

Alexander Claes Alexander Claes 5 min read
Learning Java
An amber cube shown bare and enclosed in a blue transparent cube, with arrows representing boxing and unboxing.

Why does Java have both int and Integer? They can represent the same number, but they are not the same kind of type. Understanding the difference makes boxing, unboxing, and some surprising null errors much easier to follow.

From a primitive value to an object

A primitive variable holds a value directly. A wrapper variable holds a reference to an object representing a primitive value. For example, int is a primitive type, while Integer is its wrapper class.

int score = 42;
Integer wrappedScore = Integer.valueOf(score);

Both represent 42. The second line obtains an Integer representing that number. Think of this as putting a value in a box—but remember that this is an analogy, not a mutable storage container.

The eight primitive–wrapper pairs

Primitive   Wrapper
byte        Byte
short       Short
int         Integer
long        Long
float       Float
double      Double
char        Character
boolean     Boolean

Most names simply begin with a capital letter. The exceptions worth remembering are int → Integer and char → Character. These classes belong to java.lang, so ordinary Java code can use them without import statements.

Boxing: representing a primitive as a wrapper

Boxing converts a primitive value to its corresponding wrapper type. Java can do this automatically when the surrounding code expects the wrapper; that convenience is called autoboxing.

int score = 42;
Integer wrappedScore = score; // Autoboxing

For this assignment, you can understand the automatic step as Integer.valueOf(score). You do not need to write it yourself. Boxing does not mean Java must allocate a fresh object every time; existing wrapper instances may be reused.

Unboxing: getting the primitive value back

Unboxing goes in the opposite direction. Java reads the primitive value represented by a wrapper.

Integer wrappedScore = Integer.valueOf(42);
int score = wrappedScore; // Automatic unboxing

// The explicit equivalent:
int anotherScore = wrappedScore.intValue();

All three variables represent 42, but only wrappedScore is a reference variable. The conversion changes how the value is represented, not its numerical meaning. Oracle’s boxing and unboxing tutorial explains these automatic conversions.

Why wrappers exist: a first look at collections

A collection groups multiple elements together. A List is one kind of collection: it keeps elements in order and allows duplicates. You do not need to understand the whole collections framework yet to follow this example.

In ordinary Java generics, the type between angle brackets must be a reference type. That is why we write List<Integer>, not List<int>. Here, the angle brackets say what kind of elements the list contains.

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

List<Integer> scores = new ArrayList<>();
scores.add(42);              // int is boxed to Integer
int firstScore = scores.get(0); // Integer is unboxed to int

System.out.println(firstScore); // 42

The list stores references to Integer objects. Autoboxing lets us add a primitive-looking value, and unboxing lets us retrieve it into an int variable. Index 0 means the first element.

Wrappers can be null; primitives cannot

A wrapper reference can also hold null, meaning that it refers to no object. This can represent an absent value, but it introduces a case that a primitive does not have.

int quantity = 0;           // A known quantity: zero
Integer unknown = null;    // No Integer object
// int invalid = null;     // Does not compile

Zero and missing are different ideas. If an order contains zero items, its quantity is known. If the quantity has not been supplied, using zero as a substitute may hide that distinction.

The danger appears when Java tries to unbox a null reference.

Integer quantity = null;
int total = quantity; // NullPointerException

Java needs an int value, but there is no Integer object from which to read one. Arithmetic can trigger the same conversion, as can a Boolean condition.

Boolean enabled = null;
// if (enabled) { ... } // Unboxing null also fails here

Handle missing values according to their meaning. Supply a default when absence is expected, or reject missing required data. The language specification defines unboxing and its null behavior.

Changing a variable does not change a wrapper object

The primitive wrapper classes are immutable: their represented value cannot be changed after construction. Reassigning a variable changes which object it refers to; it does not modify an existing wrapper.

Integer first = 10;
Integer second = first;
first = 11;

System.out.println(first);  // 11
System.out.println(second); // 10

The same idea applies to first++: Java unboxes the number, adds one, and boxes the result for assignment. It does not mutate the Integer shared with second.

Compare values, not wrapper identities

With two primitive ints, == compares numbers. With two Integer references, it compares whether they refer to the same object. Reused wrapper instances can make identity comparisons seem to work for some values, so do not use them as a numerical equality test.

Integer first = Integer.valueOf(500);
Integer second = Integer.valueOf(500);

System.out.println(first.equals(second)); // true

If either reference might be null, java.util.Objects.equals(first, second) handles those null cases. Also keep the types consistent: Integer.valueOf(5).equals(Long.valueOf(5L)) is false, because these are different wrapper types. The Integer equality contract is explicit about this.

Parsing is different from boxing

Turning text into a number is called parsing. It is a different operation from boxing a number you already have.

int parsed = Integer.parseInt("42");       // Text → primitive
Integer boxed = Integer.valueOf(parsed);   // Primitive → wrapper
Integer fromText = Integer.valueOf("42");  // Text → wrapper

The first and third lines interpret characters as a number. Invalid integer text, such as "hello", throws NumberFormatException. The middle line already has a number; it only obtains its wrapper representation.

Which one should you use?

Use primitives for ordinary required numbers and flags. They keep the model simple and avoid accidental null unboxing. Use wrappers when an API requires objects, when working with generic collections, or when absence is genuinely part of the data model.

Do not choose a wrapper just because its name looks more object-oriented. Nor should you convert a missing value to zero or false without considering what that would mean.

The mental model to keep

A primitive is a value; a wrapper is an object representing that value. Boxing moves from the primitive representation to the wrapper representation. Unboxing reads the primitive value back. Java often inserts these steps automatically, but they still matter—especially when null is involved.

Next, read Understanding null and NullPointerException in Java to see how missing references cause failures and how to handle them intentionally.