Java Fundamentals

Wrapper Classes & Autoboxing

15 min Lesson 9 of 14

Wrapper Classes & Autoboxing

Java's primitive types — int, double, boolean, and the rest — are fast and memory-efficient, but they are not objects. That becomes a problem the moment you need to store them in a collection, pass them to a method that expects Object, or call a utility method on a number. Wrapper classes solve this by wrapping each primitive in a full object.

The Eight Wrapper Classes

Every primitive has a corresponding class in java.lang:

  • byteByte
  • shortShort
  • intInteger
  • longLong
  • floatFloat
  • doubleDouble
  • charCharacter
  • booleanBoolean

You will work with Integer, Double, and Boolean most often. They are the ones you encounter every day in real Java code.

Autoboxing and Unboxing

Before Java 5 you had to convert manually. Modern Java does it for you automatically:

  • Autoboxing — the compiler converts a primitive to its wrapper object automatically.
  • Unboxing — the compiler converts a wrapper object back to its primitive automatically.
import java.util.ArrayList; import java.util.List; public class AutoboxDemo { public static void main(String[] args) { // Autoboxing: int -> Integer Integer boxed = 42; // compiler writes: Integer.valueOf(42) // Unboxing: Integer -> int int primitive = boxed; // compiler writes: boxed.intValue() // Works seamlessly with collections List<Integer> numbers = new ArrayList<>(); numbers.add(10); // autoboxed numbers.add(20); // autoboxed int sum = numbers.get(0) + numbers.get(1); // both unboxed System.out.println(sum); // 30 } }

The conversion is invisible at the source level, but the compiler inserts the calls. Understanding this matters for performance and for a subtle bug you will see shortly.

Useful Methods on Wrapper Classes

Wrapper classes are not just containers — they carry static utility methods that primitives cannot have.

public class WrapperMethods { public static void main(String[] args) { // --- Integer --- System.out.println(Integer.MAX_VALUE); // 2147483647 System.out.println(Integer.MIN_VALUE); // -2147483648 System.out.println(Integer.toBinaryString(10)); // 1010 System.out.println(Integer.toHexString(255)); // ff System.out.println(Integer.bitCount(7)); // 3 (three 1-bits) // --- Double --- System.out.println(Double.isNaN(0.0 / 0.0)); // true System.out.println(Double.isInfinite(1.0 / 0.0)); // true // --- Boolean --- System.out.println(Boolean.logicalAnd(true, false)); // false System.out.println(Boolean.toString(true)); // "true" } }

Parsing Strings into Primitives

One of the most common uses of wrapper classes is parsing user input or configuration values stored as text into usable numbers or booleans.

public class ParseDemo { public static void main(String[] args) { int age = Integer.parseInt("25"); double price = Double.parseDouble("19.99"); boolean flag = Boolean.parseBoolean("true"); // case-insensitive System.out.println(age + 1); // 26 System.out.println(price * 2); // 39.98 System.out.println(!flag); // false // Convert the other way: primitive or wrapper -> String String s1 = Integer.toString(100); // "100" String s2 = String.valueOf(3.14); // "3.14" System.out.println(s1 + " / " + s2); // "100 / 3.14" } }
parseInt vs valueOf: Integer.parseInt("42") returns an int primitive. Integer.valueOf("42") returns an Integer object (using the cache described below). Prefer parseInt when you need a primitive; prefer valueOf when you need an object.

The Integer Cache Pitfall

Here is one of the most famous beginner traps in Java. The JVM caches Integer objects for values between -128 and 127 inclusive. When you autobox a number in that range, you get back the same cached object every time. Outside that range a new object is created each time.

public class CachePitfall { public static void main(String[] args) { Integer a = 100; Integer b = 100; System.out.println(a == b); // true -- same cached object Integer x = 200; Integer y = 200; System.out.println(x == y); // false -- different objects! System.out.println(x.equals(y)); // true -- correct comparison } }
Never compare wrapper objects with ==. The == operator checks reference identity (are these the same object in memory?), not value equality. Two Integer objects holding 200 are different objects and == returns false. Always use .equals() to compare wrapper values.

Null and NullPointerException During Unboxing

Because wrapper objects are real objects, they can be null. Unboxing a null throws a NullPointerException — a common source of crashes when working with collections or method return values.

public class NullUnbox { public static void main(String[] args) { Integer value = null; // This line throws NullPointerException at runtime: // int n = value; // unboxing null // Safe pattern: check first if (value != null) { int n = value; System.out.println(n); } else { System.out.println("value is null"); } } }
Why does this happen? Unboxing is sugar for calling value.intValue(). Calling any method on null throws NullPointerException. The compiler cannot warn you because the assignment looks legal — always guard wrapper references that might be null.

Summary

Wrapper classes bridge primitives and the object world. Autoboxing and unboxing let you write natural-looking code while the compiler handles conversions. Keep three rules in mind: use parseInt / parseDouble to convert strings to values; always compare wrappers with .equals(), never ==; and guard against null before unboxing. With these habits you will avoid the most common wrapper-related bugs.