Chapter 31:Wrapper Classes in Java: An In-Depth Guide for Interviews

Introduction

Wrapper classes in Java serve the critical purpose of converting primitive data types into objects. This allows developers to use object-oriented programming concepts seamlessly with primitives, while also leveraging utility methods for efficient coding.


Why Do We Need Wrapper Classes?

The primary reasons for the existence of wrapper classes are:

  1. Storing Primitive Data as Objects: Some data structures, like collections, work only with objects.

  2. Utility Methods: Wrapper classes come with built-in methods that simplify operations on primitive data types.

  3. Object-Oriented Flexibility: Java is an object-oriented language, and wrapper classes help incorporate primitive data types into this paradigm.


Hierarchy of Wrapper Classes

All wrapper classes are part of the java.lang package and extend the Object class. Below is the hierarchy structure:

Object Class Hierarchy:

       Object
        |
   -------------------
   |                 |
 String        Wrapper Classes
                     |
     -----------------------------------------
     |                |                      |
  Boolean          Character           Number
                                        |
                  ----------------------------------
                  |       |      |      |        |
                Byte   Short  Integer  Long   Float/Double

Key Commands to Explore Wrapper Classes

You can inspect the methods and details of wrapper classes using the javap command. For instance, examining the Integer class:

javap java.lang.Integer

This command reveals methods like valueOf(), parseXxx(), and toString() available in the Integer wrapper class.


Important Methods in Wrapper Classes

valueOf() Method in Java


1. What is the valueOf() Method?

1.1. The valueOf() method is used to:

  • Convert primitive type data or strings to wrapper objects.

  • Provide an alternative to the constructors of wrapper classes (e.g., Integer(int)).

1.2. Why is it preferred?

  • More efficient than constructors due to caching.

  • Ensures type safety during conversions.

1.3. Wrapper Classes Supporting valueOf():

  • Every wrapper class (except Character) has a static valueOf() method.

1.4. Forms of the valueOf() Method:

  1. Primitive to Wrapper:
    Example: Integer.valueOf(int)

  2. String to Wrapper:
    Example: Integer.valueOf(String)

  3. String with Radix to Wrapper:
    Example: Integer.valueOf(String, int radix)


2. Primitive to Wrapper Conversion

2.1. Method Signature:

public static java.lang.Integer valueOf(int);

2.2. Example of Primitive to Wrapper Conversion:

Integer i = Integer.valueOf(10);
System.out.println(i); // Output: 10
  • Explanation: The primitive int value 10 is converted to an Integer object.

2.3. Why is this method preferred?

  • Uses cached objects for common values (e.g., -128 to 127 for Integer).

2.4. Example Illustrating Caching:

Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b); // Output: true

Integer c = Integer.valueOf(200);
Integer d = Integer.valueOf(200);
System.out.println(c == d); // Output: false
  • Reason: Values between -128 and 127 are cached, reducing memory usage.

3. String to Wrapper Conversion

3.1. Method Signature:

public static java.lang.Integer valueOf(String);

3.2. Example of String to Wrapper Conversion:

Integer i = Integer.valueOf("10");
System.out.println(i); // Output: 10
  • Explanation: The string "10" is converted to an Integer object.

3.3. Invalid Input Handling:

  • If a non-numeric string (e.g., "ten") is passed, it throws a NumberFormatException.

3.4. Example of Exception Handling:

try {
    Integer i = Integer.valueOf("ten");
} catch (NumberFormatException e) {
    System.out.println("Error: " + e.getMessage());
}
// Output: Error: For input string: "ten"

4. String with Radix Conversion

4.1. Method Signature:

public static java.lang.Integer valueOf(String, int radix);

4.2. What is Radix?

  • Radix represents the base of the number system.

  • Common Radices:

    • Base 2 (Binary): Digits 0-1.

    • Base 8 (Octal): Digits 0-7.

    • Base 10 (Decimal): Digits 0-9.

    • Base 16 (Hexadecimal): Digits 0-9, A-F.

4.3. Example with Base 2:

Integer binary = Integer.valueOf("100", 2);
System.out.println(binary); // Output: 4
  • Explanation: The binary string "100" is interpreted as 4 in decimal.

4.4. Example with Base 16:

Integer hex = Integer.valueOf("A", 16);
System.out.println(hex); // Output: 10

4.5. Invalid Radix Handling:

  • Radix must be between Character.MIN_RADIX (2) and Character.MAX_RADIX (36).

  • Example:

try {
    Integer invalid = Integer.valueOf("100", 37);
} catch (NumberFormatException e) {
    System.out.println("Error: " + e.getMessage());
}
// Output: Error: radix 37 greater than Character.MAX_RADIX

5. Understanding Character.MIN_RADIX and Character.MAX_RADIX

5.1. Definition:

  • Character.MIN_RADIX: The smallest allowed radix (2).

  • Character.MAX_RADIX: The largest allowed radix (36).

5.2. How to Find Radices:

System.out.println("MIN_RADIX: " + Character.MIN_RADIX); // Output: 2
System.out.println("MAX_RADIX: " + Character.MAX_RADIX); // Output: 36

5.3. Why Radices are Configured for 2 to 36:

  • Ensures compatibility with number systems commonly used in computing.

6. Advanced Use Cases

6.1. Converting Binary to Decimal:

Integer binary = Integer.valueOf("1101", 2);
System.out.println(binary); // Output: 13

6.2. Converting Hexadecimal to Decimal:

Integer hex = Integer.valueOf("1A", 16);
System.out.println(hex); // Output: 26

6.3. Unsupported Bases:

  • Attempting to convert using unsupported bases throws an exception:
Integer i = Integer.valueOf("123", 40); // Invalid radix
// Throws NumberFormatException

7. Limitations of valueOf()

7.1. Performance Considerations:

  • For larger ranges beyond the cache limit, objects are created every time.

7.2. Error-Prone Input:

  • Passing invalid strings can lead to runtime exceptions.

8. Conclusion

  • The valueOf() method is a powerful tool for creating wrapper objects from primitives or strings.

  • By understanding its nuances, including caching, radix handling, and input validation, developers can use it effectively in Java applications.