Wrapper Class in Java

If primitive data types are comparatively faster, then why do we need Wrapper Classes in Java? Why does Java Collections Framework accept Integer, Double, and Character and not int, double, or char? Does using Wrapper Classes somehow affect the performance and memory management of the system?

Java programmers generally ignore these types of questions, which can simply lead to challenges in type conversions and Autoboxing and Unboxing. Java Wrapper Classes generally bridge this gap between primitives and objects and enable Java features like Collections, Serialization, and Multithreading.

In this Java Wrapper Class Tutorial, you will learn about Java Wrapper Classes, including their advantages, performance comparisons, and best practices. By the time you complete this tutorial, you will know when to use these Wrapper classes and when to avoid them.

Table of Contents

What Are Wrapper Classes in Java?

Wrapper Classes in Java are a type of class that generally represent primitive data types as objects. Originally, primitives like int, double, char, etc., are not objects. Java typically provides corresponding Wrapper Classes like Integer, Double, Character, etc., that can handle collections, serialization, and multithreading.

There is an equivalent Wrapper Class for every primitive in java.lang package.

Primitive TypeWrapper Class
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

These Java Wrapper Classes make it possible to use primitives where objects are required, such as the Java Collections Framework, Reflection API, and Functional Programming.

Why Do We Need a Wrapper Class in Java?

If data types like primitive are faster, why do we need Wrapper classes in Java? That is because of Java’s object-oriented nature.

Major Reasons for Using Wrapper Classes

  1. Compatibility with Collections: Java’s List, Set, and Map work with objects and not primitives. It would not be possible to store int values in an ArrayList without a Wrapper Class in Java.
  2. Autoboxing and Unboxing: Java automatically converts primitives to objects (Autoboxing) and objects to primitives (Unboxing) to simplify coding.
  3. Utility Methods: Wrapper classes in Java provide methods like Integer.parseInt(), Double.valueOf(), and Boolean.parseBoolean() to convert string to numbers.
  4. Null Handling: Wrapper Classes in Java do support nulls, in contrast to primitives, which is necessary for API and database operations.
  5. Multi-Threading and Synchronization: Certain Java APIs demand references to objects, and hence, Wrapper Classes in Java are required in multi-threading.
  6. Serialization: Primitives are not serializable, but a Wrapper Class in Java is serializable and can be stored or transferred through a network.

Primitive Data Types vs Wrapper Classes

Now let’s compare primitives and Wrapper Classes in Java on key factors:

FeaturePrimitive Data TypesWrapper Classes
StorageStored in a stack (faster)Stored in heap (slower)
Memory UsageRequires less memoryRequires more memory due to object overhead
PerformanceFaster computationSlower due to object creation
NullabilityCannot be nullCan be null, useful in databases
Use in CollectionsNot supportedFully supported
Utility MethodsNot availableparseXxx(), valueOf(), compareTo() methods available

When to Use Each?

  • Always use Primitives for performance-intensive operations like complex computations and loops.
  • Make use of Java Wrapper Class majorly while using collections, databases, serialization, or API that require objects.

Understanding this comparison will generally help you in writing effective Java programs with no unnecessary performance overheads.

Understanding the Wrapper Class Flow

In Java Wrapper Classes, there are different operations that are carried out in a flow that typically involves installation, autoboxing, unboxing, calling methods, and memory management. It is very important to know about this process in order to create efficient and optimized Java code.

The Java Wrapper Class flow typically comprises these steps:

  1. Declaration and Instantiation of Wrapper Objects
  2. Autoboxing: Converting Wrapper to Primitive
  3. Unboxing: Converting Wrapper to Primitive
  4. Operations Using Wrapper Class Methods
  5. Memory Management and Object Caching

1. Declaration and Instantiation of Wrapper Objects

Wrapper classes in Java generally encapsulate the primitive values in objects. Basically, Java has two ways to create Wrapper objects:

  • Using a constructor (Deprecated in Java since Java 9)
  • Using static factory methods (valueOf())

Example: Creating Wrapper Objects

// Using the constructor (Not recommended)

Integer obj1 = new Integer(10);
// Using valueOf() (Recommended)

Integer obj2 = Integer.valueOf(10);

Why do you need to prefer valueOf() over new?

  • valueOf() uses an internal cache to optimize memory.
  • new Integer(10) always creates a new object, increasing heap usage.

2. Autoboxing in Java

Autoboxing is a mechanism through which the Java compiler changes a primitive data type to its respective Wrapper Class type. Java has introduced this feature in Java version 5 to simplify operations on collections and generics and object-based API, since previously primitives were incompatible with them.

In Java before Java 5, it was required to convert primitives to Wrapper objects explicitly using methods like Integer.valueOf(int). Autoboxing eliminates this need and enhances code readability and maintainability.

Example of Autoboxing:

int num = 10;  

Integer obj = num;  // Autoboxing: int -> Integer

How Does Autoboxing Work Internally?

When the Java compiler notices the assignment of a primitive to a Wrapper class, it internally utilizes:

Integer obj = Integer.valueOf(num);  // Explicit method call by Java internally

Here, valueOf(int) is a static factory method that returns an Integer object for the specified primitive num.

3. Unboxing in Java

The reverse process of Autoboxing is unboxing. Here, a Wrapper Class object is transformed back to its corresponding primitive type.

In Java before Java 5, you would need to get the underlying primitive value out of the wrapper explicitly using methods like intValue(), doubleValue(), etc. Unboxing facilitates this by allowing direct assignment.

Example of Unboxing:

Integer obj = 20;  

int num = obj;  // Unboxing: Integer -> int

How Does Unboxing Work Internally?

Behind the scenes, Java applies:

int num = obj.intValue();  // Explicit method call by Java internally

Here, intValue() returns the int value that is stored in the Integer object.

4. Operations Using Wrapper Class Methods

Java Wrapper classes generally provide many different utility methods for primitive conversion, parsing, and arithmetic operations.

Example: Parsing Strings into Primitives

Java

Output:

Operations Using Wrapper Class Methods

Flow of Parsing (parseInt() method):

  1. The string is verified for a numeric format.
  2. The value is obtained and then converted to a primitive.
  3. The variable is returned to its original primitive value.

Example: Converting Primitives to Strings

Java

Output:

Converting Primitives to Strings

5. Memory Management and Object Caching in Wrapper Classes

As Wrapper objects in Java are typically immutable, there can be excessive creation of objects that can simply lead to high memory usage. Java typically optimizes this by caching Wrapper objects that are used frequently.

Integer Cache Mechanism in the Wrapper class in Java

Java

Output:

Integer Cache Mechanism in the Wrapper class in Java

Flow of Integer Caching in Java:

  1. If the value is between -128 and 127, the object is then retrieved from the cache.
  2. If it is out of range, a new object is created.
  3. The same cached value is reused for subsequent uses.

Example: No Caching for Large Values

Java

Output:

No Caching for Large Values

Since 200 is outside the cache range, two separate objects are created.

Features of Java Wrapper Classes

  • Encapsulation of Primitive Data Types: Java Wrapper Classes generally convert primitives to objects in order to make them usable in object-oriented programming.
  • Autoboxing and Unboxing: Type conversions between primitive and wrapper classes are done automatically.
  • Utility Methods for Parsing and Conversion: It also provides methods like parseInt(), toString(), and valueOf() for type conversion.
  • Immutability: Wrapper classes are generally immutable to provide consistent data.
  • Support for Object-Oriented Features: It also permits primitives to co-exist with Java Collections, Generics, and frameworks.
  • Caching Mechanism: Values between -128 and 127 are cached to enhance the utilization of memory.
  • Compatibility with Java Collections Framework: It allows for primitive type values to be stored in collections like ArrayList and HashMap.
  • Thread-Safeness: Immutability of Wrapper Classes of Java typically ensures safe use in multi-threaded environments

Common Methods in Wrapper Classes

Java Wrapper Classes provide a number of utility methods for type conversions, comparison, parsing, and mathematics. With these methods, operations on primitives become simple. Below are some of the commonly used methods in classes like Integer, Double, Float, Character, and Boolean.

1. Value Conversion Methods

These are used to convert a wrapper object to a primitive type.

MethodDescriptionExample
byteValue()Returns the value as a byteInteger obj = 10; byte b = obj.byteValue();
shortValue()Returns the value as a shortShort s = obj.shortValue();
intValue()Returns the value as an intint num = obj.intValue();
longValue()Returns the value as a longlong l = obj.longValue();
floatValue()Returns the value as a floatfloat f = obj.floatValue();
doubleValue()Returns the value as a doubledouble d = obj.doubleValue();

2. Parsing and String Conversion Methods

These Java Wrapper class methods are generally used to convert primitives to Strings and vice versa.

MethodDescriptionExample
parseInt(String s)Converts a String to an intint num = Integer.parseInt(“100”);
parseDouble(String s)Converts a String to a doubledouble d = Double.parseDouble(“10.5”);
toString()Converts a primitive type into a StringString str = Integer.toString(50);
valueOf(String s)Converts a String to a wrapper objectInteger obj = Integer.valueOf(“100”);

3. Methods of Comparison and Equality

These operations enable comparisons between primitive and wrapper objects.

MethodDescriptionExample
compareTo(T obj)Compares two wrapper objectsInteger a = 10, b = 20; int result = a.compareTo(b);
equals(Object obj)Checks if two wrapper objects are equalBoolean b1 = true, b2 = true; boolean result = b1.equals(b2);
compare(x, y)Compares two primitive valuesint result = Integer.compare(10, 20);

4. Bitwise and Mathematical Operations

These operations are used to perform numerical and bitwise operations on wrapper objects.

MethodDescriptionExample
sum(x, y)Returns the sum of two valuesint total = Integer.sum(10, 20);
max(x, y)Returns the maximum of two valuesint maxVal = Integer.max(10, 20);
min(x, y)Returns the minimum of two valuesint minVal = Integer.min(10, 20);
bitCount(int i)Returns the number of 1-bits in the two’s complement binary form of an integerint count = Integer.bitCount(5);
reverse(int i)Returns the value with bits reversedint reversed = Integer.reverse(5);

5. Verifying Numeric and Character Attributes

These methods establish character and numerical properties.

MethodDescriptionExample
isDigit(char ch)Checks if a character is a digitboolean result = Character.isDigit(‘9’);
isLetter(char ch)Checks if a character is a letterboolean result = Character.isLetter(‘A’);
isLowerCase(char ch)Checks if a character is lowercaseboolean result = Character.isLowerCase(‘a’);
isUpperCase(char ch)Checks if a character is uppercaseboolean result = Character.isUpperCase(‘B’);
isWhitespace(char ch)Checks if a character is a whitespaceboolean result = Character.isWhitespace(‘ ‘);

Java Wrapper Classes vs. Primitive Data Types: Performance Comparison

AspectPrimitive Data TypesWrapper Classes
Memory UsageLow (stores actual value)High (stores object reference and metadata)
PerformanceFast (direct memory access)Slow (due to object creation and method calls)
ImmutabilityNot applicable (values can change)Immutable (cannot change once created)
Autoboxing & UnboxingNot requiredRequired, causing additional overhead
Storage in CollectionsNot possible (only objects allowed)Possible (wrapper classes are objects)
Method SupportNo built-in methodsProvides utility methods for conversion, comparison, and parsing
Garbage CollectionNot applicableGenerates extra memory usage due to object creation
Use CaseBest for performance-critical applicationsUseful for Java Collections, Generics, and frameworks

Wrapper classes in Java are convenient but come with a penalty on performance. Primitive types are space and speed-efficient and are used for computations and applications requiring high speed.

Java Wrapper Classes and Collections Framework

1. Why Are Wrapper Classes Needed in Collections?

The Java Collections Framework (ArrayList, HashMap, HashSet, etc.) is based on objects and not on primitive data types. Since primitive types (int, double, char, etc.) are not objects, Java provides Wrapper Classes (Integer, Double, Character, etc.) to make them usable in collections.

Without a wrapper class in Java, there would be no way to place primitive values into collections.

2. Using Wrapper Classes in Collections

2.1 Storing Primitives in Lists

ArrayList<int> is not used, but ArrayList<Integer> is legal due to autoboxing.

Example:

Java

Output:

Storing Primitives in Lists

Autoboxing makes it convenient to convert primitives and objects while working with collections!

2.2 Wrapper Classes in Maps

Maps require key-value pairs, and both should be objects. A wrapper class in Java generally enables primitives to be used as keys or values.

Example:

Java

Output:

Wrapper Classes in Maps

Here, we are using an Integer as an object to operate smoothly in a HashMap!

3. Performance Impact of Wrapper Classes in Collections

Because wrapper objects take up more space in memory, big collections with wrapper classes can affect performance

Key Performance Issues:

  • Increased Memory Use: Holding millions of Integer objects instead of int uses more memory.
  • Unboxing: Getting values out of collections is done through unboxing and takes processing time.
  • Garbage Collection Overload: An increasing number of objects puts more load on the garbage collector.

Example of Unboxing Overhead:

Java

Output:

Example of Unboxing Overhead

Using int[] rather than ArrayList<Integer> would significantly improve performance!

Wrapper Classes and Serialization in Java

1. Understanding Serialization in Java

Serialization is employed to convert an object to a stream of bytes such that it can be stored in a file, sent across a network, or stored in a database. Deserialization is accomplished by reversing this and converting a stream of bytes to an object.

Java provides the Serializable interface to make objects serializable.

2. Are Wrapper Classes Serializable?

Yes! Java Wrapper Classes (Integer, Double, Boolean, etc.) are all implementors of the Serializable interface and are hence easily serialized and deserialized.

Example: Wrapper Class Implementing Serializable

Java

Output:

Wrapper Class Implementing Serializable

Since Integer is an implementor of Serializable, it can be read and written directly to a file.

3. Wrapper Classes in Serializable Objects

When a custom class contains fields of type wrapper classes, they are supported for serialization by default.

Example: Serialization of a Class with Wrapper Fields

Java

Output:

 Serialization of a Class with Wrapper Fields

Because both Integer and Double are serializable, the whole Employee object can be serialized without any problems!

Wrapper Classes in Functional Programming (Java 8+ Features)

From the introduction of Java 8, functional programming became the core part of the Java Programming Language. Wrapper classes in Java typically play a very important role in functional programming as they support lambda expressions, Java streams, and method references.

1. Why Are Wrapper Classes Important in Functional Programming?

Functional programming from Java 8 and above majorly depends upon Streams API, Optional, and Method References as all of which work with objects. As we discussed already that there are no objects for primitive data types in Java, and in order to use these primitives as objects, Java typically provides Wrapper classes in order to enable functional programming.

2. Wrapper Classes in Java Streams

The Streams API in Java 8 generally enables functional-style operations on Java collections and arrays. Here, Java Wrapper classes are very important because streams work with objects, not primitive types (except for specialized streams like IntStream).

Example: Mapping Integers in a Stream using Wrapper Class in Java

Java

Output:

Mapping Integers in a Stream using Wrapper Class in Java

In the code discussed above, the Java map() method simply takes a function (a lambda function) that generally acts on Integer objects using autoboxing.

3. Wrapper Classes with Optional for Null Safety

The Java 8+ Optional<T> class also helps to avoid NullPointerException by simply containing values in an Optional container.

Example: Using Optional with Wrapper Class in Java

Java

Output:

Using Optional with Wrapper Class in Java

As shown in the above code, instead of checking for null, we use Optional<Integer>, which simply reduces the risk of causing a NullPointerException.

4. Method References with Wrapper Classes

The Method references in Java typically allow us to refer to existing methods using (::). Java Wrapper classes are supported by method references, and they are best used with Streams and with functional interfaces

Example: Using Method Reference with Wrapper Class in Java

Java

Output:

Using Method Reference with Wrapper Class in Java

Instead of using n -> System.out.println(n), we use System.out::println.

5. Primitive Streams vs Wrapper Streams

Although wrapper classes in Java are typically supported by the Streams API, Java also provides special primitive streams (IntStream, DoubleStream, etc.) for better efficiency.

Example: Using IntStream Instead of Stream<Integer>

Java

Output:

Using IntStream Instead of Stream

In the above code, using IntStream avoids unnecessary boxing and unboxing and is more efficient.

Best Practices for Using Wrapper Classes in Java

Wrapper classes in Java generally provide object-oriented functionality to basic data types, but they can also create performance overhead and unintended behavior unless used properly.

Here are the best practices to follow while using a wrapper class in Java.

  • Use Primitives instead of Wrappers: Always try to use as many primitive data types as required rather than wrapper classes (Integer, Double) for better performance and less space.
  • Avoid unnecessary autoboxing and unboxing: Excessive conversions between primitives and wrappers (Integer x = 10; int y = x;) can cause performance problems.
  • Use equals() instead of == for Wrapper Class Comparisons: Wrapper objects are generally references and therefore using == compares memory locations and can produce incorrect results.
  • Take advantage of primitive streams for performance: Use IntStream, DoubleStream, etc., instead of Stream<Integer> if you want to avoid autoboxing overhead in functional programming.
  • Be careful with Null Values in Wrapper Classes: Wrapper classes are different from primitives and can hold null. This can lead to a NullPointerException unless handled.

Java Wrapper Classes and Real-Life Applications

Now, let’s discuss some of the real-world applications for the Wrapper classes in Java:

1. Storing Primitive Values in Java Collections

Since Collections in Java(ArrayList, HashMap, etc.) are typically based on objects, it is possible to store and manipulate primitive data using wrapper classes.

2. Handling Null Values in Databases and APIs

Databases and APIs usually return null. Wrapper classes can handle them safely, while primitives cannot.

3. Serialization and Deserialization

Wrapper classes are serializable in Java and can also be used to pass objects across networks, save them to files, or cache them.

4. Configuration and Reflection APIs

Some Java libraries and frameworks like Spring, SpringBoot, and Hibernate majorly use wrapper classes for dynamic method calls using Reflection and for parameters of configuration.

5. Atomic Operations and Multithreading

Java Wrapper classes typically use atomic operations such as AtomicInteger and AtomicBoolean that simply offer thread-safe changes without synchronization.

Conclusion

With this, we have come to the end of this Wrapper Class in Java Tutorial in which you have learned that Java Wrapper Classes generally bridge the gap between primitive data types and object-based programming and are hence important for collections, functional programming, serialization, and multithreading. While they are very easy to use, overuse of them leads to efficiency issues by causing unnecessary autoboxing and memory overhead.

In creating effective and optimized Java code, follow best practices like using primitives wherever possible, avoiding unnecessary boxing/unboxing, careful management of nulls, and using primitive streams.

Java Wrapper Class – FAQs

Q1. What is a Wrapper Class in Java, and why is it needed?

In Java, a Wrapper Class is typically used to convert primitive data types (int, double, etc.) into objects (Integer, Double, etc.). They are very important for storing primitives in collections in order to represent nulls, for Java Streams and Optional usage, and to support serialization and multithreading.

Q2. How do Autoboxing and Unboxing work in Java Wrapper Classes?

Autoboxing: Autoboxing in Java automatically converts a primitive type to its respective wrapper class.

Integer num = 10; // int to Integer (autoboxing)

Unboxing: Whereas Unboxing in Java generally converts a wrapper class object to a primitive type.

int x = num; // Integer to int (unboxing)
Q3. What are the common pitfalls of using Wrapper Classes in Java?

Using == instead of .equals() for comparison (can return incorrect results).

Performance overhead resulting from excessive autoboxing/unboxing.

Risk of NullPointerException while processing null values in wrapper objects.

More memory space than for primitive types.

Q4. How do Java Wrapper Classes impact performance?

Wrapper classes in Java generally use more processing time and memory due to creation and garbage collection. Primitives should be used for operations that are critical to performance unless there is a requirement for explicit object features.

Q5. How are Wrapper Classes used in Java Collections?

Java collections like ArrayList, HashMap, and HashSet contain objects and not primitives. Wrapper classes provide a way to put primitives into collections through autoboxing.

List numbers = new ArrayList<>();
numbers.add(10); // int is autoboxed to Integer

About the Author

Senior Associate - Digital Marketing

Shailesh is a Senior Editor in Digital Marketing with a passion for storytelling. His expertise lies in crafting compelling brand stories; he blends his expertise in marketing with a love for words to captivate audiences worldwide. His projects focus on innovative digital marketing ideas with strategic thought and accuracy.

Advanced Data Science AI