In Java, wrapper classes are used to represent primitive data types as objects. Every primitive data type in Java has a corresponding wrapper class in the java.lang package.
Wrapper classes allow primitives to be used as objects when necessary, such as when working with collections (like ArrayList) or generics.
Wrapper classes provide methods to perform operations on primitive types, such as parsing, conversion, and null handling, which cannot be done with the primitives directly.
In this tutorial, we'll explore Java's wrapper classes with several code examples to demonstrate how they are used and how they convert between primitive types and objects.
1. List of Java Wrapper Classes
Java provides the following wrapper classes for each primitive data type:
Primitive Type | Wrapper Class |
---|---|
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
char |
Character |
boolean |
Boolean |
2. Why Use Wrapper Classes?
Wrapper classes are useful in many scenarios:
Collections: Java collections (like ArrayList) only work with objects, so primitive types need to be wrapped in their corresponding wrapper classes.
Type conversion: Wrapper classes provide utility methods for converting between different data types (e.g., parsing strings into numbers).
Null handling: Unlike primitive types, wrapper classes can store null values, which are useful in certain contexts.
Autoboxing and Unboxing: Java automatically converts between primitives and wrapper objects (autoboxing/unboxing), making the code easier to read and write.
3. Creating Wrapper Objects (Boxing)
Wrapper objects can be created in two ways:
Using the constructor of the wrapper class.
Using static factory methods (e.g., valueOf()).
Example: Creating Wrapper Objects
public class WrapperExample { public static void main(String[] args) { // Creating wrapper objects using constructor (Deprecated in newer versions) Integer intObj1 = new Integer(100); // Deprecated in Java 9 Double doubleObj1 = new Double(5.5); // Deprecated in Java 9 // Creating wrapper objects using valueOf() (Preferred method) Integer intObj2 = Integer.valueOf(200); Double doubleObj2 = Double.valueOf(10.5); // Displaying the values System.out.println("intObj1: " + intObj1); System.out.println("doubleObj1: " + doubleObj1); System.out.println("intObj2: " + intObj2); System.out.println("doubleObj2: " + doubleObj2); } }
Explanation:
You can create wrapper objects using the constructor (deprecated in newer versions) or the preferred valueOf() method.
Wrapper objects represent the corresponding primitive values.
Output:
intObj1: 100 doubleObj1: 5.5 intObj2: 200 doubleObj2: 10.5
4. Autoboxing and Unboxing
Autoboxing is the automatic conversion of a primitive type into its corresponding wrapper class object. Unboxing is the reverse process: converting a wrapper object back into a primitive type. Java automatically performs these conversions when needed, making the code more concise.
Example: Autoboxing and Unboxing
public class AutoboxingUnboxingExample { public static void main(String[] args) { // Autoboxing: converting a primitive to a wrapper object Integer intObj = 10; // Equivalent to Integer.valueOf(10) // Unboxing: converting a wrapper object to a primitive int intValue = intObj; // Equivalent to intObj.intValue() // Displaying the values System.out.println("Wrapper object (Integer): " + intObj); System.out.println("Primitive value (int): " + intValue); } }
Explanation:
Java automatically converts int to Integer (autoboxing) and Integer to int (unboxing).
This feature makes code easier to read by reducing explicit conversions.
Output:
Wrapper object (Integer): 10 Primitive value (int): 10
5. Wrapper Class Methods
Each wrapper class provides several utility methods for manipulating and converting primitive values. Some common methods include:
parse(): Converts a String into a primitive type.
valueOf(): Converts a String or primitive into a wrapper object.
Value(): Converts the wrapper object to a primitive.
Example: Common Wrapper Class Methods
public class WrapperMethodsExample { public static void main(String[] args) { // Using parse methods to convert String to primitive types int intValue = Integer.parseInt("123"); double doubleValue = Double.parseDouble("45.67"); // Using valueOf() to create wrapper objects from strings Integer intObj = Integer.valueOf("100"); Double doubleObj = Double.valueOf("99.99"); // Converting wrapper objects to primitive types int primitiveInt = intObj.intValue(); double primitiveDouble = doubleObj.doubleValue(); // Displaying results System.out.println("Parsed int: " + intValue); System.out.println("Parsed double: " + doubleValue); System.out.println("Wrapper Integer object: " + intObj); System.out.println("Wrapper Double object: " + doubleObj); System.out.println("Primitive int from Integer object: " + primitiveInt); System.out.println("Primitive double from Double object: " + primitiveDouble); } }
Explanation:
parseInt() and parseDouble() are used to convert String values to int and double primitives, respectively.
valueOf() is used to create wrapper objects from String values.
Value() methods are used to retrieve the primitive value from wrapper objects.
Output:
Parsed int: 123 Parsed double: 45.67 Wrapper Integer object: 100 Wrapper Double object: 99.99 Primitive int from Integer object: 100 Primitive double from Double object: 99.99
6. Wrapper Classes in Collections
Java collections (such as ArrayList, HashMap, etc.) cannot store primitive types directly. Instead, wrapper classes are used to store primitive values as objects in collections.
Example: Using Wrapper Classes in Collections
import java.util.ArrayList; public class WrapperInCollectionsExample { public static void main(String[] args) { // Creating an ArrayList to store Integer objects (autoboxing will occur) ArrayList intList = new ArrayList<>(); // Adding primitive int values (autoboxing to Integer objects) intList.add(10); // Autoboxing converts int to Integer intList.add(20); intList.add(30); // Retrieving and using the values (unboxing to int) int sum = 0; for (Integer num : intList) { sum += num; // Unboxing converts Integer to int } // Displaying the sum System.out.println("Sum: " + sum); } }
Explanation:
The ArrayList stores Integer objects (not primitive int).
Autoboxing automatically converts the primitive int values into Integer objects when added to the list, and unboxing occurs when they are retrieved.
Output:
Sum: 60
7. Null Handling with Wrapper Classes
One key advantage of wrapper classes is their ability to store null values, unlike primitive types which cannot hold null. This is useful in scenarios where a value may not be present.
Example: Null Handling with Wrapper Classes
public class NullHandlingExample { public static void main(String[] args) { // Integer can hold null, but int cannot Integer nullableInt = null; // Trying to use int with null will cause NullPointerException try { int primitiveInt = nullableInt; // Unboxing a null value will throw an exception } catch (NullPointerException e) { System.out.println("Caught NullPointerException when unboxing null Integer"); } // Safe check for null before unboxing if (nullableInt != null) { int safeInt = nullableInt; // Only unbox if not null System.out.println("Safe int value: " + safeInt); } else { System.out.println("No value present"); } } }
Explanation:
Wrapper classes can hold null values, unlike primitive types.
Unboxing a null value will cause a NullPointerException, so it's important to check for null before unboxing.
Output:
Caught NullPointerException when unboxing null Integer No value present
8. Performance Considerations
While wrapper classes provide additional functionality, they come with some performance overhead compared to primitive types:
Memory Usage: Wrapper objects consume more memory than primitive types.
Autoboxing/Unboxing: Implicit conversions between primitives and wrapper objects can introduce performance costs.
Null Handling: Wrapper classes are nullable, which adds flexibility but also the risk of NullPointerException.
When performance is critical (e.g., in loops), consider using primitive types to avoid the overhead of autoboxing/unboxing.
Example: Performance Impact of Autoboxing
public class AutoboxingPerformanceExample { public static void main(String[] args) { long startTime, endTime; // Using primitive int startTime = System.nanoTime(); int sum1 = 0; for (int i = 0; i < 1000000; i++) { sum1 += i; } endTime = System.nanoTime(); System.out.println("Time with int: " + (endTime - startTime) + " ns"); // Using wrapper Integer (autoboxing/unboxing) startTime = System.nanoTime(); Integer sum2 = 0; for (int i = 0; i < 1000000; i++) { sum2 += i; // Autoboxing/unboxing occurs } endTime = System.nanoTime(); System.out.println("Time with Integer: " + (endTime - startTime) + " ns"); } }
Explanation:
The loop that uses int performs faster than the one that uses Integer because of the overhead of autoboxing/unboxing.
Sample Output:
Time with int: 116499 ns Time with Integer: 3538675 ns
Conclusion
Java wrapper classes are a powerful tool that allows you to treat primitive data types as objects. They provide utility methods for parsing, converting, and manipulating primitive data, and are essential for working with Java collections and generics.
Key Points:
Wrapper Classes: Each primitive type has a corresponding wrapper class (int โ Integer, char โ Character, etc.).
Autoboxing and Unboxing: Java automatically converts between primitives and wrapper objects.
Utility Methods: Wrapper classes provide methods like valueOf(), parse(), and Value() for conversion and manipulation.
Null Handling: Unlike primitive types, wrapper classes can store null values.
Collections: Java collections can only hold objects, so wrapper classes are used to store primitive values.
Performance Considerations: While convenient, wrapper classes can introduce performance overhead due to memory usage and autoboxing/unboxing.
By understanding and using wrapper classes effectively, you can write more flexible and object-oriented code in Java.