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.
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.
💡 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.
Each primitive maps to exactly one wrapper class. Most names just capitalise the primitive; the two exceptions are int→Integer and char→Character .
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.
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.
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.
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.
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.
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.
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.