Java Autoboxing and Unboxing
Table of Contents + β
In the last lesson you learned about Java wrapper classes, where int has a wrapper object called Integer. Do you convert between them by hand every time? No. Java usually does it for you, and that automatic swap is called autoboxing and unboxing.
π€ The problem this solves
Java keeps numbers in two worlds, and you constantly cross between them:
- Primitives like
int,double,booleanare fast and simple. - Wrapper objects like
Integer,Double,Booleanare real objects you can store in collections. - A
Listholds only objects, so a list of numbers needsInteger, but your maths uses plainint. - Without help, you would write conversion code everywhere.
Early Java really did make you convert by hand, like this.
Integer boxed = Integer.valueOf(5); // turn int into Integer by handint back = boxed.intValue(); // turn Integer into int by handSystem.out.println(back);Java now does both conversions in the background, so you write the simple version.
Output
5π¦ What autoboxing means
Autoboxing is the automatic conversion of a primitive into its wrapper object. The plain value gets wrapped in a little box, which is the object.
Here you assign an int straight into an Integer, with no conversion code.
int number = 42;Integer boxed = number; // β
autoboxing: int becomes Integer on its ownSystem.out.println(boxed);- Java sees an
intgoing into anInteger. - It quietly calls
Integer.valueOf(number)for you. - The result is an
Integerobject holding42.
Output
42You can box a literal too. Java treats it as a primitive first, then wraps it.
Integer count = 100; // β
the literal 100 is boxed into an IntegerDouble price = 19.99; // β
the literal 19.99 is boxed into a DoubleSystem.out.println(count + " and " + price);Output
100 and 19.99π What unboxing means
Unboxing is the automatic conversion of a wrapper object back into its primitive. Java does it whenever it needs a primitive but you handed it a wrapper.
Here an Integer flows back into an int.
Integer boxed = 42;int number = boxed; // β
unboxing: Integer becomes int on its ownSystem.out.println(number);- Java sees an
Integergoing into anint. - It quietly calls
boxed.intValue()for you. - The plain
42comes out of the box into yourint.
Output
42π Where it happens automatically
You never ask for boxing. Java does it in these everyday spots:
- Plain assignment:
intintoInteger, orIntegerintoint. - Collections: a
List,Map, orSetholds only objects, so primitives get boxed in and unboxed out. - Method calls: a primitive passed where a wrapper is expected gets boxed, and the reverse gets unboxed.
- Arithmetic on wrappers: Java unboxes them, does the maths, then boxes the result.
Collections are the big one. You add plain int values and read one back into a plain int.
import java.util.ArrayList;import java.util.List;
public class Main { public static void main(String[] args) { List<Integer> scores = new ArrayList<>(); scores.add(90); // β
autoboxing: int 90 becomes Integer scores.add(85); int first = scores.get(0); // β
unboxing: Integer becomes int System.out.println("First score: " + first); }}The list only ever held Integer objects, yet you wrote no conversion in either direction.
Output
First score: 90In method calls, a method that wants an Integer accepts an int because Java boxes it on the way in.
public class Main { static void show(Integer value) { // method wants an Integer System.out.println("Got: " + value); }
public static void main(String[] args) { int n = 7; show(n); // β
autoboxing: int n is boxed to match the parameter }}Output
Got: 7In arithmetic, Java cannot add two objects directly, so it unboxes both, adds, then boxes the result.
Integer a = 10;Integer b = 3;Integer sum = a + b; // β
a and b are unboxed, added, then sum is boxedSystem.out.println(sum);Output
13π₯ The NullPointerException trap
A wrapper can be null, but a primitive cannot. So unboxing a null crashes:
- There is no number in the box to pull out.
- Java tries to call
intValue()on nothing. - The program stops with a
NullPointerException.
Integer boxed = null;int number = boxed; // β unboxing null: nothing to take out, this crashesSystem.out.println(number);Output
Exception in thread "main" java.lang.NullPointerException at Main.main(Main.java:3)This hides in a Map lookup that returns null for a missing key.
import java.util.HashMap;import java.util.Map;
public class Main { public static void main(String[] args) { Map<String, Integer> ages = new HashMap<>(); ages.put("Alex", 30); int age = ages.get("Sam"); // β "Sam" is missing, get returns null, then unboxing crashes System.out.println(age); }}"Sam" is not in the map, so get returns null, and unboxing that into int age crashes. The fix is to keep it as an Integer, check for null, or use a default.
Null and primitives do not mix
A primitive can never be null. So any time you unbox a wrapper that might be null, you risk a crash. Check for null before you unbox, especially with values that came from a map, a database, or a method that can return nothing.
π The cost of boxing in tight loops
Boxing is handy but not free:
- Each box creates a new object.
- Millions of tiny objects in a loop is slow and wastes memory.
- You rarely notice, except in a loop that runs many times.
Watch this mistake. The total is Long (wrapper) instead of long (primitive).
public class Main { public static void main(String[] args) { Long sum = 0L; // β wrapper type: every step boxes a new Long for (long i = 0; i < 100_000_000L; i++) { sum += i; // unbox sum, add, box the result back, every loop } System.out.println(sum); }}Each sum += i unboxes, adds, then boxes a brand new Long. Across a hundred million loops that is a hundred million throwaway objects, which is slow and hammers the garbage collector.
Output
4999999950000000The fix is a primitive long. Same answer, no boxing.
public class Main { public static void main(String[] args) { long sum = 0L; // β
primitive type: no objects created at all for (long i = 0; i < 100_000_000L; i++) { sum += i; // plain addition, fast, no boxing } System.out.println(sum); }}Every step is plain primitive maths, so no objects are created and it runs far faster. For a value you change many times in a loop, use the primitive.
Output
4999999950000000π― The Integer cache and == again
Comparing wrappers with == is risky, and autoboxing makes the trap easy to hit:
- Java caches
Integerobjects for values-128to127. - Two boxed values in that range can be the very same object.
- Outside that range, Java makes a fresh object each time.
Integer a = 100;Integer b = 100;System.out.println(a == b); // β true here, but only because of the cache
Integer c = 200;Integer d = 200;System.out.println(c == d); // β false: two separate objects100 is cached, so a and b are the same object and == is true. 200 is outside the range, so c and d differ and == is false. Same code shape, opposite result.
Output
truefalse== checks if two references point at the same object. To compare the values inside, use .equals.
Integer c = 200;Integer d = 200;System.out.println(c.equals(d)); // β
true: compares the values.equals looks at the numbers, not at which objects they are.
Output
trueβ οΈ Common Mistakes
The boxing errors people hit most. Watch for these traps.
Unboxing a value that might be null. A null wrapper cannot become a primitive, so check first.
// β Avoid: if the value is null, this crashesint age = ages.get("Sam");
// β
Good: keep it boxed and check, or use a defaultInteger found = ages.get("Sam");int age = (found != null) ? found : 0;Using == to compare wrappers. It compares objects, not values, and the cache makes it lie for small numbers.
// β Avoid: true or false depending on the cache, never reliableif (a == b) { }
// β
Good: compare the valuesif (a.equals(b)) { }Boxing inside a hot loop. A wrapper accumulator creates a new object every step, so use the primitive.
// β Avoid: every += boxes a new LongLong sum = 0L;for (long i = 0; i < 1_000_000L; i++) sum += i;
// β
Good: plain primitive, no objectslong sum = 0L;for (long i = 0; i < 1_000_000L; i++) sum += i;β Best Practices
Habits that keep boxing safe and fast.
- Prefer primitives by default. Use
int,long,doublefor plain values and loop counters. Reach forIntegeronly when you need an object, like inside a collection. - Check for null before unboxing. Any wrapper from a map, a database, or a method that can return nothing might be
null. Guard it first. - Never use == on wrappers. Always use
.equals, since the cache makes==give different answers for different numbers. - Keep accumulators primitive. A running total or counter inside a loop should be a primitive, so no objects are created each step.
- Let autoboxing help, but know it is there. It keeps collection code clean. Just remember a hidden box or unbox is happening, so the null and performance traps stay on your radar.
π§© What Youβve Learned
Good work. Letβs recap autoboxing and unboxing.
- β
Autoboxing is the automatic conversion of a primitive into its wrapper, like
intintoInteger. - β
Unboxing is the reverse, a wrapper back into a primitive, like
Integerintoint. - β
It happens automatically in assignment, in collections like
List<Integer>, in method arguments, and in arithmetic on wrappers. - β
Unboxing a
nullwrapper throws aNullPointerException, because a primitive cannot benull. - β Boxing in a tight loop creates many throwaway objects and is slow, so use primitives for accumulators.
- β
The
Integercache (-128to127) makes==unreliable on wrappers, so compare with.equals.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What is autoboxing?
Why: Autoboxing is Java automatically wrapping a primitive like int into its wrapper object like Integer.
- 2
What happens when Java unboxes a null Integer into an int?
Why: A primitive cannot be null, so unboxing a null wrapper throws a NullPointerException.
- 3
Why is using a Long accumulator in a tight loop slow?
Why: Each += on a wrapper unboxes, adds, and boxes a new object every loop, which is slow. Use the primitive long.
- 4
Why can Integer a = 100; Integer b = 100; give a == b as true but the same with 200 give false?
Why: Values in the Integer cache range (-128 to 127) reuse the same object, so == is true. Outside it, separate objects make == false. Use .equals.
π Whatβs Next?
You now understand how Java moves values between primitives and wrappers on its own. Next, letβs do maths with these values: the operators that add, subtract, multiply, and divide, and the rules behind them.