Wrapper Classes & Autoboxing
Java's eight primitives are fast but they aren't objects — so they can't go in a List or be null . Wrapper classes like Integer wrap a primitive in an object, and autoboxing converts between the two automatically. Convenient — and full of subtle traps.
Learn Wrapper Classes & Autoboxing in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Before You Start
You'll need the primitive types (int, double, boolean, char...) and a first look at collections like ArrayList , which only hold objects — the main reason wrappers exist.
What You'll Learn
The Big Idea — A Gift Box for a Number
💡 Analogy: A primitive int is a bare coin sitting on the table — quick to grab, but it can't be labelled or shipped. An Integer is that same coin sealed in a small gift box: now it has a label, you can put it on a shelf alongside other boxes (a collection), and the box can be empty ( null ). Autoboxing is the machine that drops the coin in the box; unboxing takes it back out.
Every primitive has a matching wrapper: int→Integer , double→Double , boolean→Boolean , char→Character , long→Long , short→Short , byte→Byte , float→Float . They live in java.lang , so no import is needed.
1️⃣ The Eight Wrapper Classes
Each primitive maps to exactly one wrapper class. Most names just capitalise the primitive; the two exceptions are int→Integer and char→Character .
Primitive
Wrapper
Note
int
Integer
Name differs from the primitive
char
Character
double
Double
Just capitalised
boolean
Boolean
long
Long
short
Short
byte
Byte
float
Float
2️⃣ Autoboxing & Auto-Unboxing
Since Java 5, the compiler converts between a primitive and its wrapper automatically wherever the context demands it. Autoboxing wraps a primitive into an object; auto-unboxing pulls the primitive back out.
This is why you can write list.add(90) even though a List<Integer> only stores objects — the 90 is boxed for you. The convenience is real, but remember it's real method calls under the hood.
3️⃣ The == Trap (Use .equals!)
This is the most infamous wrapper gotcha. For memory efficiency, Java caches Integer objects for the range -128 to 127 . Two boxed values in that range share one cached object, so == (which compares references ) returns true . Outside the range, each box is a new object and == returns false — even when the numbers are equal.
4️⃣ The null Danger
A wrapper can hold null — useful for "no value yet". But the moment Java tries to auto-unbox a null wrapper into a primitive, it calls a method on null and throws a NullPointerException . This is one of the sneakiest NPEs because the unboxing is invisible in the source.
5️⃣ Free Utilities on Wrapper Classes
Wrapper classes aren't just boxes — they're toolboxes of static helpers you'll use constantly: parse text into numbers, read type limits, and convert between number bases.
- Integer.parseInt("256") — text → int (throws if not a number)
- Integer.MAX_VALUE / MIN_VALUE — the type's bounds
- Integer.toBinaryString(255) , toHexString(255) — base conversions
- Integer.compare(a, b) — returns -1, 0, or 1 for sorting
🧩 Reorder Challenge
These lines read three String prices, parse them, and print the total. They've been shuffled — find the right order.
Declare the data and accumulator, loop and parse each string with the wrapper's static method, close the loop, then print.
🧠 Quick Recall
Predict the output before opening each answer.
Answer: true false . 127 is inside the cache (-128..127) so a and b share one object. 128 is outside it, so c and d are separate objects and == compares references.
Answer: It throws a NullPointerException . count + 1 needs to auto-unbox count to an int, but it's null — so the invisible count.intValue() call blows up before anything is printed.
Answer: 84 . parseInt turns the text "42" into the int 42, which is then doubled. (If s were "4two" it would throw a NumberFormatException instead.)
🎯 Your Turn #1 — Parse and sum
Three numbers arrive as text. Parse each with a wrapper method and print the total.
🎯 Your Turn #2 — Compare correctly
The values are 500 — outside the cache — so == gives the wrong answer. Fix the comparison to compare values.
🧩 Mini-Challenge — Nullable max
Return type Integer lets you return null for an empty list — something an int can't do. Build it from the outline.
Common Errors
- ❌ Comparing wrappers with == . Works by luck for small values, fails for large ones. Fix: always use .equals() for wrapper values.
- ❌ NullPointerException from unboxing. A null Integer used in arithmetic or assigned to an int throws. Fix: null-check before unboxing, or keep the variable as a primitive.
- ❌ List<int> won't compile. Generics need object types. Fix: use List<Integer> — the elements autobox automatically.
- ❌ NumberFormatException from parseInt. Integer.parseInt("12a") throws at runtime. Fix: validate input, or wrap the call in a try-catch.
- ❌ Heavy boxing in tight loops. Integer sum = 0; inside a million-iteration loop allocates a million objects. Fix: accumulate in a primitive int and box once at the end if needed.
- ❌ Using new Integer(5) . Deprecated since Java 9 and always creates a new object (defeating the cache). Fix: use Integer.valueOf(5) or just 5 .
Pro Tips
- 💡 Default to primitives for fields and local variables; reach for wrappers only when an API or nullability demands it.
- 💡 An Optional<Integer> often communicates "value may be absent" more clearly than a nullable Integer .
- 💡 For high-performance numeric work, use primitive streams ( IntStream , DoubleStream ) to avoid boxing entirely.
- 💡 The cache range is configurable upward via -XX:AutoBoxCacheMax , but never rely on cache behaviour for correctness — always use .equals() .
📋 Quick Reference
Task
Syntax
Notes
Box a primitive
Integer i = 5;
Autoboxing — Integer.valueOf(5)
Unbox to primitive
int n = i;
Auto-unboxing — i.intValue()
Compare values
a.equals(b)
Never == for wrappers!
Parse text
Integer.parseInt("12")
NumberFormatException if bad
Type bounds
Integer.MAX_VALUE
2147483647
Collection type
List<Integer>
Wrappers only, never primitives
❓ Frequently Asked Questions
🎉 Lesson Complete!
You now know every primitive's wrapper class, how autoboxing and auto-unboxing work, why collections force wrappers on you, and the two big traps — the == cache surprise and the null-unboxing NPE. You can also reach for the handy static helpers like parseInt and MAX_VALUE .
Next up: Access Modifiers — controlling who can see and use your classes, fields, and methods with public, private, and protected.
Practice quiz
What does this print? Integer a = 100; Integer b = 100; System.out.println(a == b);
- false
- It throws an exception
- true
- 100
Answer: true. Java caches Integer values from -128 to 127, so a and b share one cached object and == returns true. This is the cache trap.
What does this print? Integer c = 200; Integer d = 200; System.out.println(c == d);
- false
- true
- 200
- It throws an exception
Answer: false. 200 is OUTSIDE the cache range (-128..127), so c and d are separate objects and == compares references, giving false. Use .equals() instead.
What is autoboxing?
- Converting a wrapper to a primitive
- Comparing two wrappers
- Parsing a String into a number
- The compiler automatically converting a primitive to its wrapper, e.g. int 5 to Integer.valueOf(5)
Answer: The compiler automatically converting a primitive to its wrapper, e.g. int 5 to Integer.valueOf(5). Autoboxing wraps a primitive into its wrapper object where an object is expected; auto-unboxing is the reverse (Integer to int via intValue()).
What happens here? Integer count = null; int n = count + 1;
- n becomes 1
- It throws a NullPointerException when auto-unboxing null
- n becomes null
- It prints 0
Answer: It throws a NullPointerException when auto-unboxing null. count + 1 must auto-unbox count to an int, calling the invisible count.intValue() on null, which throws a NullPointerException.
Why does == sometimes work and sometimes fail with Integer?
- Values -128..127 are cached and share an object, but outside that range each box is new
- It depends on the day
- == never works with Integer
- It depends on the variable names
Answer: Values -128..127 are cached and share an object, but outside that range each box is new. Cached small values share one object so == is true; outside the cache each autoboxed value is a new object so == compares references. Always use .equals().
What does Integer.toBinaryString(255) return?
- "FF"
- "255"
- "11111111"
- "0xFF"
Answer: "11111111". 255 in binary is eight 1 bits: "11111111". (Integer.toHexString(255) would give "ff".)
Why does List<Integer> work but List<int> does not?
- int is deprecated
- Generics require object types, so primitives must use their wrapper classes
- Integer is faster
- List cannot hold numbers
Answer: Generics require object types, so primitives must use their wrapper classes. Generic collections can only hold objects, not primitives, so you use List<Integer>; the int elements autobox automatically on add.
What does Integer.parseInt("256") return, and what type is it?
- "256" as a String
- 256 as an Integer object
- It throws an exception
- 256 as an int
Answer: 256 as an int. parseInt turns the text "256" into the primitive int 256. (parseInt("12a") would throw NumberFormatException.)
Why is new Integer(5) discouraged in modern Java?
- It is too slow to type
- It is deprecated since Java 9 and always creates a new object, defeating the cache
- It returns null
- It throws an exception
Answer: It is deprecated since Java 9 and always creates a new object, defeating the cache. new Integer(5) is deprecated and always allocates a fresh object. Use Integer.valueOf(5) or just 5, which can reuse the cache.
When should you prefer primitives over wrappers?
- Never - always use wrappers
- Only inside collections
- By default in loops, counters, and local variables, to avoid allocation and null/equality traps
- Only when a value may be null
Answer: By default in loops, counters, and local variables, to avoid allocation and null/equality traps. Prefer primitives by default; reach for wrappers only when an API forces it (generics/collections) or when a value genuinely needs to be nullable.
Continue this course
- Previous: Switch Statement & Switch Expressions
- Next: Access Modifiers