Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

Wrapper Classes

Every Java primitive type has a corresponding wrapper class that represents that value as a full object: byte→Byte, short→Short, int→Integer, long→Long, float→Float, double→Double, char→Character, and boolean→Boolean. Wrapper classes let primitive values participate in APIs that require objects, such as collections like ArrayList<Integer>, which cannot hold raw primitives.

Why it matters

Collections and generics work only with objects, so every int stored in a list becomes an Integer. Java converts between the two automatically, which is convenient and hides two traps: unboxing a null throws an exception, and comparing two wrapper objects with == compares references, which happens to work for small numbers and fails for large ones.

Watch: Wrapper Classes in Java Explained | Autoboxing & Unboxing | Core Java Tutorial

Covers why Java has wrapper classes, primitives vs wrappers, all eight wrapper classes, autoboxing and unboxing, the Integer, Double, Boolean and Character classes, using wrappers in collections, and common interview questions.

Watch on YouTube

Why Java Needs Wrapper Classes

Java keeps primitives such as int and double as plain values for speed, but large parts of the language work only with objects. Generics, and therefore every collection, accept object types only: List<int> does not compile, while List<Integer> does. Wrapper classes bridge that gap by putting a primitive value inside an object. They also let a value be null to mean "no value yet", which a primitive cannot express, and they give each type a home for parsing methods and constants.

Primitive Data Types vs Wrapper Classes

  • Nature: a primitive holds the raw value; a wrapper is an object on the heap that holds the value.
  • null: a primitive can never be null; a wrapper reference can.
  • Default value of a field: 0, 0.0 or false for primitives; null for wrappers.
  • Collections and generics: primitives cannot be used as type arguments; wrappers can.
  • Comparison: == compares values for primitives; for wrappers it compares references, so use equals().
  • Memory and speed: primitives are smaller and faster; each wrapper is a full object with header overhead.
  • Mutability: wrapper objects are immutable. count++ on an Integer creates a new object rather than changing the old one.

All 8 Wrapper Classes in Java

All wrapper classes live in java.lang, so no import is needed. The six numeric wrappers extend the abstract class Number, which provides intValue(), doubleValue() and the other conversion methods. Character and Boolean extend Object directly. Note the two names that differ from their primitive: Integer and Character.

  • byte → Byte
  • short → Short
  • int → Integer
  • long → Long
  • float → Float
  • double → Double
  • char → Character
  • boolean → Boolean

Autoboxing and Unboxing

Autoboxing is the compiler's automatic conversion of a primitive into its wrapper object (int → Integer), and unboxing is the reverse (Integer → int). Both happen implicitly wherever needed, such as adding an int to a List<Integer> or using an Integer in an arithmetic expression — the compiler inserts the conversion calls for you behind the scenes. Integer boxed = 5; compiles to Integer.valueOf(5), and int n = boxed; compiles to boxed.intValue(). Before Java 5 you had to write those calls yourself. The old new Integer(5) constructors are deprecated for removal; use valueOf() or plain autoboxing instead.

The Integer Caching Gotcha

Java caches Integer objects for values from -128 to 127 (and similarly caches small values for other integer wrapper types). When autoboxing a value in that range, Java may return the same cached object rather than creating a new one, so comparing two boxed values with == in that range can appear to work by coincidence. Outside that range, autoboxing creates new distinct objects, so == comparisons stop matching even for equal values — which is exactly why .equals(), not ==, should always be used to compare wrapper objects.

Integer, Double, Boolean and Character Methods

Wrapper classes also carry parsing and conversion utilities, making them the natural home for type-specific helper logic that a raw primitive cannot carry. The ones you will use most often:

  • Integer.parseInt("25") returns a primitive int; Integer.valueOf("25") returns an Integer object. Both throw NumberFormatException for text such as "25a".
  • Double.parseDouble("99.5") and Long.parseLong(...) work the same way for their types.
  • Boolean.parseBoolean(s) returns true only for "true" in any letter case; every other string, including null, gives false.
  • Character.isLetter, isDigit, isWhitespace, toUpperCase and toLowerCase classify and convert single characters.
  • Integer.MAX_VALUE, Integer.MIN_VALUE, Long.MAX_VALUE and similar constants give the range of each type.
  • Integer.compare(a, b), Integer.toBinaryString(n) and String.valueOf(n) / Integer.toString(n) compare and convert values back to text.

Wrapper Classes in Java Collections

Every number you put in an ArrayList<Integer> or a HashMap<String, Integer> is boxed on the way in and unboxed on the way out. Two details catch people out. First, List<Integer> has both remove(int index) and remove(Object value), so list.remove(1) removes the element at index 1, not the value 1; write list.remove(Integer.valueOf(1)) to remove by value. Second, map.get(key) returns null for a missing key, and assigning that to an int throws NullPointerException; use getOrDefault(key, 0) or keep the result in an Integer.

Common Interview Questions

  • What is a wrapper class? A class in java.lang that wraps one primitive value in an object, such as Integer for int.
  • Why do we need wrapper classes? Collections and generics accept only objects, wrappers can be null, and they provide parsing methods and constants.
  • What are autoboxing and unboxing? Automatic conversion from primitive to wrapper (via valueOf()) and back (via intValue() and similar), added in Java 5.
  • Difference between parseInt() and valueOf()? parseInt returns a primitive int; valueOf returns an Integer and may reuse a cached object.
  • Why is Integer a == b true for 127 but false for 128? Integer.valueOf caches -128 to 127, so small values share one object; larger ones are new objects.
  • Are wrapper classes immutable? Yes. All eight are immutable and final.
  • What happens when you unbox null? A NullPointerException is thrown.

Examples

Autoboxing, unboxing, and the Integer caching gotcha with == vs equals()

Java
public class WrapperDemo {
    public static void main(String[] args) {
        int primitive = 100;
        Integer boxed = primitive;       // autoboxing
        int unboxed = boxed;             // unboxing
        System.out.println(unboxed);

        Integer a = 100;
        Integer b = 100;
        System.out.println(a == b);      // true: both within cached range -128..127

        Integer c = 200;
        Integer d = 200;
        System.out.println(c == d);      // false: outside cached range, different objects
        System.out.println(c.equals(d)); // true: equals() compares actual value
    }
}
Output
100
true
false
true

Parsing and helper methods on Integer, Double, Boolean and Character

Java
public class WrapperMethodsDemo {
    public static void main(String[] args) {
        int age = Integer.parseInt("25");        // String -> int
        Integer boxedAge = Integer.valueOf("25"); // String -> Integer
        double price = Double.parseDouble("99.5");
        boolean active = Boolean.parseBoolean("TRUE");

        System.out.println(age + 1);
        System.out.println(boxedAge.equals(age));
        System.out.println(price * 2);
        System.out.println(active);

        System.out.println(Integer.MAX_VALUE);
        System.out.println(Integer.toBinaryString(10));
        System.out.println(Integer.compare(5, 9));

        char ch = 'x';
        System.out.println(Character.isLetter(ch) + " " + Character.isDigit('7'));
        System.out.println(Character.toUpperCase(ch));

        String text = String.valueOf(42) + Double.toString(1.5);
        System.out.println(text);
    }
}
Output
26
true
199.0
true
2147483647
1010
-1
true true
X
421.5

Wrappers in collections: boxing in a List, remove(index) vs remove(value), and null from a Map

Java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WrapperCollectionsDemo {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);                 // autoboxed to Integer.valueOf(10)
        numbers.add(20);
        numbers.add(30);

        int total = 0;
        for (int n : numbers) {          // each element is unboxed
            total += n;
        }
        System.out.println("Total: " + total);

        numbers.remove(1);                     // removes the element at index 1
        System.out.println(numbers);
        numbers.remove(Integer.valueOf(10));   // removes the value 10
        System.out.println(numbers);

        Map<Character, Integer> counts = new HashMap<>();
        for (char ch : "java".toCharArray()) {
            counts.merge(ch, 1, Integer::sum);
        }
        System.out.println(counts.get('a'));
        System.out.println(counts.get('z'));   // null, not 0
    }
}
Output
Total: 60
[10, 30]
[30]
2
null

Common Mistakes

  • Comparing wrapper objects with == expecting value comparison — it compares references, and only appears to "work" inside the cached -128 to 127 range.
  • Unboxing a null wrapper reference (e.g. a null Integer used in arithmetic), which throws a NullPointerException at runtime.
  • Repeatedly autoboxing/unboxing inside performance-critical loops without realizing each operation allocates a wrapper object, adding overhead.
  • Forgetting that primitive char autoboxes to Character, not to a numeric wrapper, since char behaves differently from the numeric primitives.
  • Calling list.remove(1) on a List<Integer> to remove the value 1 — it removes the element at index 1 instead.
  • Using the deprecated new Integer(5) constructor instead of Integer.valueOf(5) or autoboxing.

Exercise

This program prints true then false, even though both pairs hold equal numbers, because == on Integer objects compares references. Fix both comparisons so the program prints true twice.

Java · starter code
public class IntegerCompare {
    public static void main(String[] args) {
        Integer a = 127;
        Integer b = 127;
        System.out.println(a == b);

        Integer c = 128;
        Integer d = 128;
        System.out.println(c == d);
    }
}
Show hints
  • Values from -128 to 127 are cached, so a and b happen to be the same object. 128 is outside the cache.
  • Compare wrapper objects by value with equals().
Show solution
Java · solution
public class IntegerCompare {
    public static void main(String[] args) {
        Integer a = 127;
        Integer b = 127;
        System.out.println(a.equals(b)); // true

        Integer c = 128;
        Integer d = 128;
        System.out.println(c.equals(d)); // true
    }
}

Quick quiz

1. What happens with Integer x = null; int y = x;?

  • A. y becomes 0
  • B. It does not compile
  • C. It throws NullPointerException
  • D. y becomes -1
Show answer

C. Unboxing calls intValue() on the Integer. With a null reference that call throws NullPointerException.

2. Which range of values does Java cache for Integer by default?

  • A. 0 to 255
  • B. No values are cached
  • C. -1000 to 1000
  • D. -128 to 127
Show answer

D. Integer.valueOf, which autoboxing uses, returns shared objects for -128 to 127.

3. Which method returns a primitive int from the text "42"?

  • A. Integer.parseInt("42")
  • B. Integer.valueOf("42")
  • C. Integer.toString("42")
  • D. new String("42")
Show answer

A. parseInt returns an int. valueOf returns an Integer object.

Interview questions

What are autoboxing and unboxing?

Show answer

Autoboxing is the automatic conversion of a primitive to its wrapper object, such as int to Integer, when an object is required. Unboxing is the reverse. The compiler inserts calls to Integer.valueOf() and intValue() for you. It is convenient, but it creates objects, so heavy boxing in a tight loop costs performance.

Why does Java have wrapper classes?

Show answer

Because primitives are not objects. Generic types such as List<T> and Map<K, V> can only hold objects, so numbers must be wrapped to be stored in collections. Wrappers can also be null to represent "no value", and they provide utility methods and constants such as Integer.parseInt and Integer.MAX_VALUE.

Key Points to Remember

  • Each of the 8 primitives has a matching wrapper class needed for use in generic collections and APIs.
  • Autoboxing/unboxing convert between primitives and wrappers automatically.
  • Integer caches values from -128 to 127, making == unreliable outside that range; always use equals() for wrapper value comparison.
  • Wrapper classes also provide parsing methods and useful constants like MAX_VALUE and MIN_VALUE.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering