Skip to content
Java oops misc 6 min read

Wrapper Classes

Java’s primitive types (int, double, boolean, etc.) are fast and memory-efficient, but there are situations where you need an object — for example, when storing values in a collection like ArrayList. Wrapper classes solve this by wrapping each primitive type in a corresponding class that lives in java.lang.

What Are Wrapper Classes?

Every primitive type in Java has a matching wrapper class. The wrapper acts as a box around the raw value, turning it into a full-fledged object with methods you can call.

PrimitiveWrapper Class
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

All numeric wrapper classes extend Number, which is worth knowing when you work with generic numeric code.

Creating Wrapper Objects

Since Java 9, the constructors (new Integer(42)) are deprecated. Use the static factory method valueOf() instead — it is more efficient because it can return a cached instance.

Integer a = Integer.valueOf(42);
Double  b = Double.valueOf(3.14);
Boolean c = Boolean.valueOf(true);
Character d = Character.valueOf('Z');

System.out.println(a); // 42
System.out.println(b); // 3.14
System.out.println(c); // true
System.out.println(d); // Z

Output:

42
3.14
true
Z

Autoboxing and Unboxing

Since Java 5 the compiler handles conversions between primitives and wrappers automatically. This feature is called autoboxing (primitive → wrapper) and unboxing (wrapper → primitive).

// Autoboxing: int is automatically wrapped into Integer
Integer x = 100;

// Unboxing: Integer is automatically unwrapped back to int
int y = x;

System.out.println(x + " " + y); // 100 100

Output:

100 100

Autoboxing makes it natural to put primitives into collections:

import java.util.ArrayList;

ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);  // autoboxed to Integer.valueOf(10)
numbers.add(20);
numbers.add(30);

int sum = 0;
for (int n : numbers) { // unboxed automatically
    sum += n;
}
System.out.println("Sum: " + sum);

Output:

Sum: 60

Note: Without wrapper classes, ArrayList<int> would be a compile error — generics only work with reference types, not primitives. For high-performance use cases, consider primitive-specialized collections from libraries like Eclipse Collections.

Parsing Strings to Primitives

One of the most common reasons to reach for a wrapper class is parsing user input or text data into numbers.

String ageText  = "25";
String priceText = "9.99";
String flagText  = "true";

int    age   = Integer.parseInt(ageText);
double price = Double.parseDouble(priceText);
boolean flag = Boolean.parseBoolean(flagText);

System.out.println(age   + 1);   // 26
System.out.println(price * 2);   // 19.98
System.out.println(!flag);       // false

Output:

26
19.98
false

Warning: Integer.parseInt("abc") throws a NumberFormatException at runtime. Always validate input or wrap the call in a try-catch. See Exception Handling for patterns.

Converting Numbers to Strings

The reverse direction is equally easy:

int n = 42;
String s1 = Integer.toString(n);   // "42"
String s2 = String.valueOf(n);      // "42" (works for any primitive)
String s3 = "" + n;                 // "42" via concatenation

System.out.println(s1 + " " + s2 + " " + s3);

Output:

42 42 42

Useful Utility Methods

Each wrapper class ships with a rich set of static utility methods. Here are some you’ll reach for regularly:

Integer Utilities

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.toOctalString(8));   // 10
System.out.println(Integer.bitCount(7));        // 3  (bits set in 0b111)
System.out.println(Integer.reverse(1));         // large number (bit reversal)
System.out.println(Integer.compare(5, 10));     // negative (5 < 10)

Character Utilities

char ch = 'A';
System.out.println(Character.isLetter(ch));     // true
System.out.println(Character.isDigit(ch));      // false
System.out.println(Character.isUpperCase(ch));  // true
System.out.println(Character.toLowerCase(ch));  // a
System.out.println(Character.isWhitespace(' ')); // true

Double Utilities

System.out.println(Double.isNaN(0.0 / 0.0));      // true
System.out.println(Double.isInfinite(1.0 / 0.0)); // true
System.out.println(Double.MAX_VALUE);              // 1.7976931348623157E308

Comparing Wrapper Objects

Warning: Never use == to compare wrapper objects — it checks reference equality, not value equality. For Integer values between −128 and 127 it might appear to work (due to the integer cache), but it will silently fail for values outside that range.

Integer p = 1000;
Integer q = 1000;

System.out.println(p == q);      // false  — different objects
System.out.println(p.equals(q)); // true   — same value
System.out.println(Integer.compare(p, q)); // 0  — equal

Always use .equals() or the static Integer.compare() / Comparator.naturalOrder() methods when comparing wrapper values. This is especially important when sorting — see Comparator.

Null Handling — a Gotcha with Unboxing

Because wrapper objects are reference types, they can be null. Unboxing a null wrapper throws a NullPointerException.

Integer value = null;
int result = value; // NullPointerException at runtime!

Guard against this wherever nullable wrappers flow through your code:

Integer value = null;
int result = (value != null) ? value : 0;
System.out.println(result); // 0

Optional from Java 8 is another clean way to handle the possibility of an absent value.

Under the Hood

Integer Cache

The JVM caches Integer objects for values between −128 and 127 (configurable up to 127+ via -XX:AutoBoxCacheMax). Calls to Integer.valueOf(n) within this range return the same cached object. That’s why Integer.valueOf(100) == Integer.valueOf(100) is true but Integer.valueOf(1000) == Integer.valueOf(1000) is false. Byte, Short, Long, and Character (0–127) have similar caches.

Memory Cost

A boxed Integer on the heap takes around 16 bytes (object header) plus 4 bytes for the int field — roughly 5× the cost of a plain int. In tight loops or large arrays this overhead adds up. That’s why Java has kept primitives in the language, and why Java 21 features (Project Valhalla, moving toward value types) aim to close this gap.

Bytecode

When the compiler sees autoboxing, it silently inserts a invokestatic Integer.valueOf(I)Ljava/lang/Integer; call. Unboxing becomes invokevirtual Integer.intValue()I. You can verify this with the javap tool:

// Source: Integer x = 5;
0: iconst_5
1: invokestatic  #2  // Method Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1

Understanding this helps you write performance-conscious code — unnecessary boxing in hot loops is a real (though often avoidable) cost.

Immutability

All wrapper objects are immutable. Once created, the value inside cannot change. This makes them safe to share across threads without synchronisation, and is also why the integer cache is sound. If you need a mutable counter in concurrent code, look at java.util.concurrent.atomic.AtomicInteger instead.

Quick Reference

// Parse
int i    = Integer.parseInt("123");
long l   = Long.parseLong("9876543210");
double d = Double.parseDouble("3.14");

// Convert to String
String s = Integer.toString(42);
String h = Integer.toHexString(255); // "ff"
String b = Integer.toBinaryString(5); // "101"

// Limits
int max  = Integer.MAX_VALUE;   // 2_147_483_647
long lmax = Long.MAX_VALUE;

// Check special values
boolean nan = Double.isNaN(Double.NaN);
boolean inf = Double.isInfinite(1.0 / 0.0);

// Character checks
boolean isLetter = Character.isLetter('a');
char upper = Character.toUpperCase('z'); // 'Z'
  • Autoboxing & Unboxing — a deeper look at the compiler magic behind primitive-to-object conversions
  • Generics — why type parameters require reference types and how wrapper classes make collections type-safe
  • ArrayList — the most common place you’ll rely on wrapper classes in everyday code
  • Data Types — the primitive types that each wrapper class corresponds to
  • Optional — a modern alternative to nullable wrappers when a value might be absent
  • Comparable — wrapper classes implement Comparable, enabling natural ordering in sorted collections
Last updated June 13, 2026
Was this helpful?