Java Autoboxing & Unboxing - Complete Guide
Autoboxing & Unboxing in Java
Autoboxing and unboxing are automatic conversions between primitive types and their corresponding wrapper classes, introduced in Java 5.
What is Autoboxing?
Autoboxing is the automatic conversion of primitive types to their corresponding wrapper class objects.
Simple Explanation: Java automatically wraps primitive values (like int, double) into their object versions (like Integer, Double) when needed.
1public class AutoboxingIntro {
2 public static void main(String[] args) {
3 // ========== BEFORE JAVA 5 (Manual Boxing) ==========
4
5 // Had to manually convert primitive to wrapper
6 int primitiveInt = 10;
7 Integer wrapperInt = Integer.valueOf(primitiveInt); // Manual!
8
9 System.out.println(wrapperInt); // Output: 10
10
11 // ========== JAVA 5+ (Autoboxing) ==========
12
13 // Automatic conversion - compiler does it for you
14 Integer autoBoxed = 10; // int → Integer (automatic!)
15
16 System.out.println(autoBoxed); // Output: 10
17
18 // Both approaches produce the same result
19 // But autoboxing is more convenient
20 }
21}What this program does:
- ›Line 7: Creates a primitive
intwith value 10 - ›Line 8: Manually converts it to an
Integerwrapper object (old way) - ›Line 13: Creates an
Integerobject directly fromint(autoboxing - new way) - ›Result: Both methods create an Integer object with value 10, but autoboxing is simpler
Key Point: The compiler automatically calls Integer.valueOf(10) behind the scenes when you write Integer autoBoxed = 10;
What is Unboxing?
Unboxing is the automatic conversion of wrapper class objects to their corresponding primitive types.
Simple Explanation: Java automatically extracts the primitive value from wrapper objects when needed.
1public class UnboxingIntro {
2 public static void main(String[] args) {
3 // ========== BEFORE JAVA 5 (Manual Unboxing) ==========
4
5 Integer wrapperInt = Integer.valueOf(100);
6 int primitiveInt = wrapperInt.intValue(); // Manual!
7
8 System.out.println(primitiveInt); // Output: 100
9
10 // ========== JAVA 5+ (Unboxing) ==========
11
12 Integer wrapper = 100;
13 int primitive = wrapper; // Integer → int (automatic!)
14
15 System.out.println(primitive); // Output: 100
16
17 // Compiler automatically calls intValue()
18 }
19}What this program does:
- ›Line 6: Creates an Integer wrapper object with value 100
- ›Line 7: Manually extracts the primitive
intvalue usingintValue()(old way) - ›Line 12: Creates an Integer object (autoboxing happens here too!)
- ›Line 13: Automatically extracts primitive
intfrom Integer (unboxing - new way) - ›Result: Both methods get the primitive int value 100, but unboxing is simpler
Key Point: The compiler automatically calls wrapper.intValue() behind the scenes when you write int primitive = wrapper;
How Autoboxing Works Internally
When you write autoboxing code, the compiler transforms it behind the scenes.
Simple Explanation: The compiler secretly adds method calls to convert your primitives to wrapper objects.
1public class InternalMechanism {
2 public static void main(String[] args) {
3 // ========== WHAT YOU WRITE ==========
4 Integer num = 10;
5
6 // ========== WHAT COMPILER ACTUALLY DOES ==========
7 // Integer num = Integer.valueOf(10);
8
9 // valueOf() method is called automatically by the compiler
10
11 // ========== PROOF THAT THEY'RE THE SAME ==========
12
13 Integer a = 100; // Autoboxing
14 Integer b = Integer.valueOf(100); // Manual boxing
15
16 System.out.println(a == b); // Output: true (same cached object)
17
18 // ========== ALL WRAPPER CLASSES WORK THE SAME WAY ==========
19
20 // What you write → What compiler does
21 Double d1 = 3.14; // → Double.valueOf(3.14)
22 Boolean b1 = true; // → Boolean.valueOf(true)
23 Character c1 = 'A'; // → Character.valueOf('A')
24 Long l1 = 1000L; // → Long.valueOf(1000L)
25 }
26}What this program does:
- ›Line 7: You write
Integer num = 10;(looks simple) - ›Line 10: But compiler changes it to
Integer.valueOf(10)behind the scenes - ›Lines 14-17: Proves both approaches create the exact same object
- ›Lines 21-24: Shows this works for all wrapper types (Double, Boolean, Character, Long)
Why this matters: Understanding this helps you know what's really happening in your code.
How Unboxing Works Internally
Simple Explanation: The compiler secretly adds method calls to extract primitive values from wrapper objects.
1public class UnboxingMechanism {
2 public static void main(String[] args) {
3 // ========== WHAT YOU WRITE ==========
4 Integer wrapper = 100;
5 int primitive = wrapper;
6
7 // ========== WHAT COMPILER ACTUALLY DOES ==========
8 // int primitive = wrapper.intValue();
9
10 // ========== PROOF THAT THEY'RE THE SAME ==========
11
12 Integer num = 50;
13 int a = num; // Unboxing (automatic)
14 int b = num.intValue(); // Manual unboxing
15
16 System.out.println(a == b); // Output: true (same value)
17
18 // ========== ALL WRAPPER CLASSES WORK THE SAME WAY ==========
19
20 // What you write → What compiler does
21 Double d = 3.14;
22 double d_val = d; // → d.doubleValue()
23
24 Boolean bool = true;
25 boolean b_val = bool; // → bool.booleanValue()
26
27 Character ch = 'A';
28 char c_val = ch; // → ch.charValue()
29 }
30}What this program does:
- ›Lines 6-7: You write
int primitive = wrapper;(looks simple) - ›Line 10: But compiler changes it to
wrapper.intValue()behind the scenes - ›Lines 14-17: Proves both approaches give the same primitive value
- ›Lines 21-28: Shows this works for all wrapper types with their respective methods
Key Methods:
- ›
Integer→intValue() - ›
Double→doubleValue() - ›
Boolean→booleanValue() - ›
Character→charValue()
When Autoboxing Happens
Autoboxing occurs in several contexts. Let's see each one with clear examples.
1. Assignment (Direct Assignment to Wrapper Variable)
Simple Explanation: When you assign a primitive value to a wrapper variable, autoboxing happens automatically.
1public class AssignmentAutoboxing {
2 public static void main(String[] args) {
3 // Direct assignment - autoboxing happens here
4 Integer num = 10; // int → Integer
5 Double price = 19.99; // double → Double
6 Boolean flag = true; // boolean → Boolean
7 Character letter = 'A'; // char → Character
8
9 System.out.println(num); // Output: 10
10 System.out.println(price); // Output: 19.99
11 System.out.println(flag); // Output: true
12 System.out.println(letter); // Output: A
13 }
14}What this program does:
- ›Line 6: Assigns primitive
int10 toIntegervariable → autoboxing to Integer.valueOf(10) - ›Line 7: Assigns primitive
double19.99 toDoublevariable → autoboxing - ›Line 8: Assigns primitive
booleantrue toBooleanvariable → autoboxing - ›Line 9: Assigns primitive
char'A' toCharactervariable → autoboxing - ›Result: All primitive values are automatically wrapped into their object forms
Real-world use: This is the most common place you'll see autoboxing - when storing primitives in wrapper variables.
2. Method Arguments (Passing Primitives to Methods That Expect Wrappers)
Simple Explanation: When a method expects a wrapper type but you pass a primitive, autoboxing converts it automatically.
1public class MethodAutoboxing {
2 // Method expecting Integer wrapper (not primitive int)
3 static void printInteger(Integer num) {
4 System.out.println("Number: " + num);
5 }
6
7 // Method expecting Double wrapper
8 static void processDouble(Double value) {
9 System.out.println("Value: " + value);
10 }
11
12 public static void main(String[] args) {
13 // Passing primitives - autoboxing happens automatically
14 printInteger(42); // int → Integer (autoboxing)
15 processDouble(3.14); // double → Double (autoboxing)
16
17 // What compiler actually does:
18 // printInteger(Integer.valueOf(42));
19 // processDouble(Double.valueOf(3.14));
20 }
21}What this program does:
- ›Lines 3-5: Defines a method that accepts
Integer(wrapper), notint(primitive) - ›Lines 8-10: Defines a method that accepts
Double(wrapper), notdouble(primitive) - ›Line 15: Calls
printInteger()with primitive42→ autoboxing converts it to Integer - ›Line 16: Calls
processDouble()with primitive3.14→ autoboxing converts it to Double - ›Result: You can pass primitives to methods expecting wrappers - Java handles conversion
Real-world use: Common when working with APIs that use wrapper types (like Collections).
3. Collections (Adding Primitives to Collections)
Simple Explanation: Collections like ArrayList can only store objects, not primitives. Autoboxing automatically converts primitives to wrappers when you add them.
1import java.util.ArrayList;
2
3public class CollectionAutoboxing {
4 public static void main(String[] args) {
5 // ArrayList can ONLY store objects (not primitives)
6 // So we use wrapper class Integer
7 ArrayList<Integer> numbers = new ArrayList<>();
8
9 // Adding primitives - autoboxing happens automatically
10 numbers.add(10); // int → Integer (autoboxing)
11 numbers.add(20); // int → Integer
12 numbers.add(30); // int → Integer
13
14 // What compiler actually does behind the scenes:
15 // numbers.add(Integer.valueOf(10));
16 // numbers.add(Integer.valueOf(20));
17 // numbers.add(Integer.valueOf(30));
18
19 System.out.println(numbers); // Output: [10, 20, 30]
20 }
21}What this program does:
- ›Line 7: Creates an ArrayList that stores
Integerobjects (not primitiveint) - ›Lines 10-12: You add primitive
intvalues (10, 20, 30) - ›Behind the scenes: Java automatically converts each
inttoIntegerusing autoboxing - ›Line 19: Prints the ArrayList containing three Integer objects
- ›Result: You can add primitives to collections - Java wraps them automatically
Why this is important: Collections (ArrayList, HashMap, etc.) cannot store primitives. Autoboxing lets you use them as if they could!
Real-world use: Every time you add numbers to an ArrayList, autoboxing is happening.
4. Return Statements (Returning Primitives from Methods That Return Wrappers)
Simple Explanation: When a method is supposed to return a wrapper type but you return a primitive, autoboxing converts it.
1public class ReturnAutoboxing {
2 // Method says it returns Integer wrapper (not primitive int)
3 static Integer getAge() {
4 return 25; // Returning primitive int → autoboxing to Integer
5 }
6
7 // Method returns Double wrapper
8 static Double getPrice() {
9 return 19.99; // Returning primitive double → autoboxing to Double
10 }
11
12 // Method returns Boolean wrapper
13 static Boolean isActive() {
14 return true; // Returning primitive boolean → autoboxing to Boolean
15 }
16
17 public static void main(String[] args) {
18 Integer age = getAge(); // Receives Integer object
19 Double price = getPrice(); // Receives Double object
20 Boolean active = isActive(); // Receives Boolean object
21
22 System.out.println("Age: " + age); // Output: Age: 25
23 System.out.println("Price: " + price); // Output: Price: 19.99
24 System.out.println("Active: " + active); // Output: Active: true
25 }
26}What this program does:
- ›Lines 3-5: Method signature says it returns
Integer, but line 4 returns primitiveint 25→ autoboxing - ›Lines 8-10: Method signature says
Double, but returns primitivedouble→ autoboxing - ›Lines 13-15: Method signature says
Boolean, but returns primitiveboolean→ autoboxing - ›Lines 18-20: Call these methods and receive wrapper objects
- ›Result: You can return primitives from methods that expect wrappers - Java wraps them
Real-world use: Convenient when building APIs that use wrapper types but work with primitive values internally.
5. Operators and Expressions (Arithmetic Operations with Wrappers)
Simple Explanation: When you do math with wrapper objects, Java unboxes them, does the math, then autoboxes the result.
1public class OperatorAutoboxing {
2 public static void main(String[] args) {
3 Integer a = 10;
4 Integer b = 20;
5
6 // Addition - complex autoboxing/unboxing happening!
7 Integer sum = a + b;
8
9 // What compiler actually does (step by step):
10 // Step 1: Unbox a → int temp1 = a.intValue(); → temp1 = 10
11 // Step 2: Unbox b → int temp2 = b.intValue(); → temp2 = 20
12 // Step 3: Add → int temp3 = temp1 + temp2; → temp3 = 30
13 // Step 4: Autobox → Integer sum = Integer.valueOf(temp3); → sum = Integer(30)
14
15 System.out.println(sum); // Output: 30
16
17 // ALL arithmetic operations work this way:
18
19 Integer result1 = a * 2; // Unbox a, multiply by 2, autobox result
20 Integer result2 = a - 5; // Unbox a, subtract 5, autobox result
21 Integer result3 = b / 4; // Unbox b, divide by 4, autobox result
22
23 System.out.println(result1); // Output: 20 (10 * 2)
24 System.out.println(result2); // Output: 5 (10 - 5)
25 System.out.println(result3); // Output: 5 (20 / 4)
26 }
27}What this program does:
- ›Lines 3-4: Create two Integer wrapper objects (autoboxing happens here)
- ›Line 7: Try to add two Integer objects → Can't add objects directly!
- ›Lines 10-13: Show the 4 steps Java performs:
- ›Unbox both Integers to get primitive ints
- ›Add the primitive ints
- ›Autobox the result back to Integer
- ›Lines 19-21: Show same process works for multiply, subtract, divide
- ›Result: You can do math with wrapper objects - Java handles all conversions
Important: This involves BOTH unboxing (to do math) and autoboxing (to store result). That's why it's slower than using primitives!
When Unboxing Happens
Unboxing occurs in similar contexts to autoboxing. Let's see each one clearly.
1. Assignment to Primitive (Assigning Wrapper to Primitive Variable)
Simple Explanation: When you assign a wrapper object to a primitive variable, unboxing extracts the primitive value automatically.
1public class AssignmentUnboxing {
2 public static void main(String[] args) {
3 // Create wrapper objects first (autoboxing)
4 Integer wrapper = 100;
5 Double dWrapper = 3.14;
6 Boolean bWrapper = true;
7
8 // Assign to primitives - unboxing happens
9 int primitive = wrapper; // Integer → int (unboxing)
10 double dPrimitive = dWrapper; // Double → double (unboxing)
11 boolean bPrimitive = bWrapper;// Boolean → boolean (unboxing)
12
13 System.out.println(primitive); // Output: 100
14 System.out.println(dPrimitive); // Output: 3.14
15 System.out.println(bPrimitive); // Output: true
16 }
17}What this program does:
- ›Lines 4-6: Create wrapper objects (Integer, Double, Boolean)
- ›Line 9: Assign Integer to
intvariable → Java callswrapper.intValue()automatically - ›Line 10: Assign Double to
doublevariable → Java callsdWrapper.doubleValue() - ›Line 11: Assign Boolean to
booleanvariable → Java callsbWrapper.booleanValue() - ›Result: Wrapper objects are automatically unwrapped to get their primitive values
Real-world use: Common when getting values from collections (which store wrappers) into primitive variables.
2. Method Arguments (Passing Wrappers to Methods That Expect Primitives)
Simple Explanation: When a method expects a primitive but you pass a wrapper, unboxing extracts the value automatically.
1public class MethodUnboxing {
2 // Method expecting primitive int (not Integer wrapper)
3 static void printInt(int num) {
4 System.out.println("Number: " + num);
5 }
6
7 // Method expecting primitive double
8 static void processDouble(double value) {
9 System.out.println("Value: " + value);
10 }
11
12 public static void main(String[] args) {
13 Integer wrapper = 42; // Wrapper object
14 Double dWrapper = 3.14; // Wrapper object
15
16 // Passing wrappers to methods that expect primitives
17 printInt(wrapper); // Integer → int (unboxing)
18 processDouble(dWrapper); // Double → double (unboxing)
19
20 // What compiler actually does:
21 // printInt(wrapper.intValue());
22 // processDouble(dWrapper.doubleValue());
23 }
24}What this program does:
- ›Lines 3-5: Define method that accepts primitive
int, not Integer wrapper - ›Lines 8-10: Define method that accepts primitive
double, not Double wrapper - ›Lines 13-14: Create Integer and Double wrapper objects
- ›Line 17: Pass Integer wrapper to method expecting
int→ unboxing happens - ›Line 18: Pass Double wrapper to method expecting
double→ unboxing happens - ›Result: You can pass wrappers to methods expecting primitives - Java extracts the value
Real-world use: Useful when working with collections (which store wrappers) but calling methods that need primitives.
3. Arithmetic Operations (Math with Wrapper Objects)
Simple Explanation: You can't do math directly with objects. Java automatically unboxes wrappers to primitives, does the math, then stores the result.
1public class ArithmeticUnboxing {
2 public static void main(String[] args) {
3 Integer a = 50;
4 Integer b = 30;
5
6 // Arithmetic requires primitives - unboxing happens automatically
7 int sum = a + b; // Both unbox, then add
8 int diff = a - b; // Both unbox, then subtract
9 int product = a * b; // Both unbox, then multiply
10 int quotient = a / b; // Both unbox, then divide
11
12 System.out.println("Sum: " + sum); // Output: 80
13 System.out.println("Difference: " + diff); // Output: 20
14 System.out.println("Product: " + product); // Output: 1500
15 System.out.println("Quotient: " + quotient); // Output: 1
16
17 // INCREMENT/DECREMENT also causes unboxing then autoboxing
18 Integer count = 10;
19 count++; // Unbox to int, increment, autobox back to Integer
20
21 // What compiler does:
22 // count = Integer.valueOf(count.intValue() + 1);
23
24 System.out.println(count); // Output: 11
25 }
26}What this program does:
- ›Lines 3-4: Create two Integer wrapper objects
- ›Line 7: To add
a + b, Java must unbox both toint, then add - ›Lines 8-10: Same process for subtraction, multiplication, division
- ›Line 18:
count++is complex - unboxes, increments, then autoboxes back - ›Result: You can do math with wrappers, but Java converts them to primitives first
Important: Arithmetic operations REQUIRE primitives. Wrappers are automatically unboxed.
4. Comparison Operations (Comparing Wrapper Objects)
Simple Explanation: When using comparison operators (<, >, <=, >=) with wrappers, Java unboxes them to compare the primitive values.
1public class ComparisonUnboxing {
2 public static void main(String[] args) {
3 Integer a = 100;
4 Integer b = 200;
5
6 // Relational operators - unboxing happens for comparison
7 boolean isLess = a < b; // Both unbox to int for comparison
8 boolean isGreater = a > b; // Both unbox to int
9
10 System.out.println("a < b: " + isLess); // Output: true
11 System.out.println("a > b: " + isGreater); // Output: false
12
13 // IMPORTANT: == compares object REFERENCES, not values!
14 boolean isEqual = a == b;
15 System.out.println("a == b: " + isEqual); // Output: false (different objects)
16
17 // Use .equals() to compare VALUES
18 System.out.println("a.equals(b): " + a.equals(b)); // Output: false
19
20 // CACHED VALUES (special case: -128 to 127)
21 Integer x = 50;
22 Integer y = 50;
23 System.out.println(x == y); // Output: true (same cached object)
24
25 // NON-CACHED VALUES (outside -128 to 127)
26 Integer m = 1000;
27 Integer n = 1000;
28 System.out.println(m == n); // Output: false (different objects)
29 }
30}What this program does:
- ›Lines 3-4: Create two Integer objects
- ›Lines 7-8: Use
<and>operators → Java unboxes both tointfor comparison - ›Line 13: Use
==operator → This compares OBJECT REFERENCES, not values! - ›Line 17: Use
.equals()→ This compares actual VALUES (correct way) - ›Lines 20-22: Small values (-128 to 127) are cached, so
==works - ›Lines 25-27: Large values create new objects, so
==gives false - ›Result: For < > <= >=, unboxing happens. For ==, it compares references!
Critical Rule:
- ›Use
<,>,<=,>=for numeric comparison (unboxing happens) - ›Use
.equals()for value equality (don't use==) - ›
==compares references, not values!
5. Conditional Expressions (Using Wrappers in if/while/for Statements)
Simple Explanation: When you use Boolean wrapper in conditions (if, while), Java automatically unboxes it to get the primitive boolean value.
1public class ConditionalUnboxing {
2 public static void main(String[] args) {
3 Boolean flag = true; // Boolean wrapper object
4
5 // In if condition - unboxing happens
6 if (flag) { // Boolean → boolean (unboxing)
7 System.out.println("Flag is true");
8 }
9
10 // What compiler actually does:
11 // if (flag.booleanValue()) { ... }
12
13 // WHILE LOOP example
14 Boolean condition = true;
15 int count = 0;
16
17 while (condition && count < 3) { // Unboxing Boolean to boolean
18 System.out.println("Count: " + count);
19 count++;
20 if (count >= 3) condition = false; // Autoboxing boolean to Boolean
21 }
22
23 // TERNARY OPERATOR example
24 Integer age = 20;
25 String status = (age >= 18) ? "Adult" : "Minor"; // age unboxes to int
26 System.out.println(status); // Output: Adult
27 }
28}What this program does:
- ›Line 3: Create Boolean wrapper object with value
true - ›Line 6: Use Boolean in if statement → Java calls
flag.booleanValue()to get primitive - ›Lines 14-20: Use Boolean in while loop → Unboxing happens to check condition
- ›Line 23: Use Integer in ternary operator → Unboxes to int for comparison with 18
- ›Result: Wrapper objects can be used in conditionals - Java automatically gets the primitive value
Output:
Flag is true
Count: 0
Count: 1
Count: 2
Adult
Real-world use: Common when working with Boolean objects from databases or APIs in conditional logic.
Mixed Operations (Both Autoboxing AND Unboxing Together)
Simple Explanation: Often, both autoboxing and unboxing happen in the same line of code. Understanding this helps you see what's really happening.
1public class MixedOperations {
2 public static void main(String[] args) {
3 // ========== EXAMPLE 1: ARITHMETIC WITH WRAPPERS ==========
4
5 Integer a = 10; // Autoboxing: int 10 → Integer
6 int b = 20; // Primitive int
7
8 Integer result = a + b; // MIXED OPERATION - Let's break it down:
9
10 // Step 1: Unbox 'a' → a.intValue() gives us int 10
11 // Step 2: Add primitives → 10 + 20 = 30 (primitive int)
12 // Step 3: Autobox result → Integer.valueOf(30) → Integer object
13 // Step 4: Store in 'result' variable
14
15 System.out.println(result); // Output: 30
16
17 // ========== EXAMPLE 2: COLLECTIONS ==========
18
19 ArrayList<Integer> numbers = new ArrayList<>();
20 numbers.add(5); // Autobox: int 5 → Integer.valueOf(5)
21
22 int value = numbers.get(0); // Unbox: Integer → int
23
24 int doubled = value * 2; // Primitive arithmetic (no boxing)
25 numbers.set(0, doubled); // Autobox: int doubled → Integer
26
27 System.out.println(numbers.get(0)); // Output: 10
28
29 // ========== EXAMPLE 3: INCREMENT OPERATOR ==========
30
31 Integer count = 100;
32 count++; // VERY complex! Let's break it down:
33
34 // Step 1: Unbox count → count.intValue() = 100
35 // Step 2: Increment → 100 + 1 = 101 (primitive)
36 // Step 3: Autobox → Integer.valueOf(101)
37 // Step 4: Assign back → count = Integer(101)
38
39 System.out.println(count); // Output: 101
40
41 // ========== EXAMPLE 4: COMPOUND ASSIGNMENT ==========
42
43 Integer sum = 50;
44 sum += 25; // Similar to count++ - multiple steps:
45
46 // Step 1: Unbox sum → sum.intValue() = 50
47 // Step 2: Add 25 → 50 + 25 = 75 (primitive)
48 // Step 3: Autobox → Integer.valueOf(75)
49 // Step 4: Assign → sum = Integer(75)
50
51 System.out.println(sum); // Output: 75
52 }
53}What this program does:
Example 1 (Lines 5-15):
- ›Creates Integer wrapper and primitive int
- ›Adds them together (requires unboxing Integer, then autoboxing result)
- ›Shows the 4 steps that happen automatically
Example 2 (Lines 19-27):
- ›Demonstrates collection operations
- ›
add()requires autoboxing to store in ArrayList - ›
get()returns Integer which unboxes when assigned toint - ›
set()requires autoboxing to store back
Example 3 (Lines 31-39):
- ›Shows that
count++is very complex - ›Unboxes, increments, autoboxes, and reassigns
- ›All this happens in one short statement!
Example 4 (Lines 43-51):
- ›Shows
+=operator does same thing - ›Multiple conversions in one line
Why this matters: Understanding these hidden operations helps you:
- ›Know why wrapper arithmetic is slower than primitive
- ›Understand potential NullPointerExceptions
- ›Write more efficient code when needed
Performance Implications
Autoboxing/unboxing has performance costs:
1public class PerformanceDemo {
2 public static void main(String[] args) {
3 // ========== POOR PERFORMANCE ==========
4
5 long start1 = System.currentTimeMillis();
6
7 Integer sum1 = 0; // Wrapper class
8 for (int i = 0; i < 1_000_000; i++) {
9 sum1 += i; // Unbox, add, autobox - 1 million times!
10 }
11
12 long end1 = System.currentTimeMillis();
13 System.out.println("With autoboxing: " + (end1 - start1) + "ms");
14 System.out.println("Sum: " + sum1);
15
16 // ========== GOOD PERFORMANCE ==========
17
18 long start2 = System.currentTimeMillis();
19
20 int sum2 = 0; // Primitive
21 for (int i = 0; i < 1_000_000; i++) {
22 sum2 += i; // Pure primitive arithmetic
23 }
24
25 long end2 = System.currentTimeMillis();
26 System.out.println("With primitives: " + (end2 - start2) + "ms");
27 System.out.println("Sum: " + sum2);
28
29 // Typical output:
30 // With autoboxing: 50-100ms
31 // With primitives: 2-5ms
32
33 // ========== WHY IT'S SLOWER ==========
34
35 // Each iteration with wrapper:
36 // 1. sum1.intValue() - method call
37 // 2. Add operation
38 // 3. Integer.valueOf() - method call + possible object creation
39 // 4. Assignment
40
41 // With primitive:
42 // 1. Add operation
43 // 2. Assignment
44
45 // Result: 10-20x slower with wrappers!
46 }
47}Performance Best Practices
1public class PerformanceBestPractices {
2 public static void main(String[] args) {
3 // ❌ BAD - Unnecessary autoboxing in loops
4 Integer total = 0;
5 for (int i = 0; i < 1000; i++) {
6 total += i; // Unbox, add, autobox each iteration
7 }
8
9 // ✅ GOOD - Use primitive in loops
10 int total2 = 0;
11 for (int i = 0; i < 1000; i++) {
12 total2 += i; // Pure primitive operation
13 }
14 Integer finalTotal = total2; // Convert once at the end
15
16 // ❌ BAD - Wrapper in arithmetic-heavy code
17 public static Integer calculateSum(Integer a, Integer b, Integer c) {
18 return a + b + c; // Multiple unbox/autobox operations
19 }
20
21 // ✅ GOOD - Primitive in arithmetic
22 public static int calculateSum(int a, int b, int c) {
23 return a + b + c; // Pure primitive arithmetic
24 }
25
26 // ❌ BAD - Using wrapper in array processing
27 Integer[] numbers = {1, 2, 3, 4, 5};
28 Integer sum = 0;
29 for (Integer num : numbers) {
30 sum += num; // Unbox num, unbox sum, add, autobox
31 }
32
33 // ✅ GOOD - Use primitive array
34 int[] numbers2 = {1, 2, 3, 4, 5};
35 int sum2 = 0;
36 for (int num : numbers2) {
37 sum2 += num; // Pure primitive
38 }
39 }
40}The NullPointerException Problem
THE MOST DANGEROUS ASPECT OF AUTOBOXING/UNBOXING
Simple Explanation: Wrapper objects can be null, but primitives cannot. When Java tries to unbox a null wrapper, it crashes with NullPointerException.
1public class NullPointerProblem {
2 public static void main(String[] args) {
3 // ========== THE PROBLEM ==========
4
5 Integer value = null; // Wrapper CAN be null (primitives cannot!)
6
7 try {
8 int result = value + 10; // CRASH! NullPointerException!
9
10 // Why it crashes:
11 // Compiler tries: value.intValue() + 10
12 // But value is NULL, so calling .intValue() on null = CRASH!
13
14 } catch (NullPointerException e) {
15 System.out.println("Error: Cannot unbox null");
16 }
17
18 // ========== MORE DANGEROUS EXAMPLES ==========
19
20 // Example 1: Arithmetic on null wrapper
21 Integer a = null;
22 try {
23 int sum = a + 5; // NullPointerException!
24 // Tries to call: a.intValue() + 5
25 // But a is null!
26 } catch (NullPointerException e) {
27 System.out.println("Example 1: Arithmetic on null wrapper");
28 }
29
30 // Example 2: Comparison with null wrapper
31 Integer b = null;
32 try {
33 if (b > 10) { // NullPointerException!
34 // Tries to call: b.intValue() > 10
35 // But b is null!
36 System.out.println("Greater");
37 }
38 } catch (NullPointerException e) {
39 System.out.println("Example 2: Comparison with null wrapper");
40 }
41
42 // Example 3: Method parameter unboxing
43 Integer c = null;
44 try {
45 printDouble(c); // NullPointerException inside method!
46 } catch (NullPointerException e) {
47 System.out.println("Example 3: Method parameter unboxing");
48 }
49
50 // Example 4: Null in Collections
51 ArrayList<Integer> numbers = new ArrayList<>();
52 numbers.add(10);
53 numbers.add(null); // ArrayList ALLOWS null!
54 numbers.add(20);
55
56 try {
57 int sum = 0;
58 for (Integer num : numbers) {
59 sum += num; // NullPointerException when num is null!
60 // Tries to call: num.intValue()
61 // But one num is null!
62 }
63 } catch (NullPointerException e) {
64 System.out.println("Example 4: Null in collection");
65 }
66 }
67
68 // Method expecting primitive int
69 static void printDouble(int num) {
70 System.out.println(num * 2);
71 // When you pass null Integer, it tries to unbox it first!
72 }
73}What this program does:
- ›
Lines 5-15: Shows THE main problem
- ›
valueisnull - ›Trying to add to it requires unboxing
- ›Java tries to call
value.intValue()but value is null → CRASH!
- ›
- ›
Lines 19-28: Example 1 - Same problem with arithmetic
- ›
Lines 30-40: Example 2 - Same problem with comparisons
- ›
Lines 42-48: Example 3 - Crash happens inside the method when parameter unboxes
- ›
Lines 50-63: Example 4 - Very dangerous! ArrayList accepts null, but crashes when you try to use it
Output:
Error: Cannot unbox null
Example 1: Arithmetic on null wrapper
Example 2: Comparison with null wrapper
Example 3: Method parameter unboxing
Example 4: Null in collection
Why this is dangerous:
- ›Wrapper objects can be
null(unlike primitives) - ›When unboxing happens automatically, you might not notice the null check is needed
- ›The crash might happen far from where the null was created
Real-world scenario:
1// Getting data from database - might be null!
2Integer age = database.getUserAge(userId); // Could return null!
3int ageNextYear = age + 1; // CRASH if age is null!Safe Null Handling
Simple Explanation: Always check for null before using wrapper objects that might be null.
1public class SafeNullHandling {
2 public static void main(String[] args) {
3 // ========== APPROACH 1: NULL CHECK BEFORE USE ==========
4
5 Integer value = null;
6
7 if (value != null) {
8 int result = value + 10; // Safe - only runs if not null
9 System.out.println(result);
10 } else {
11 System.out.println("Value is null - cannot calculate");
12 }
13
14 // ========== APPROACH 2: DEFAULT VALUE ==========
15
16 Integer score = null;
17 int finalScore = (score != null) ? score : 0; // Use 0 if null
18 System.out.println("Final score: " + finalScore); // Output: 0
19
20 // ========== APPROACH 3: OPTIONAL (Java 8+) ==========
21
22 Integer age = null;
23 int safeAge = Optional.ofNullable(age).orElse(18); // Use 18 if null
24 System.out.println("Age: " + safeAge); // Output: 18
25
26 // ========== APPROACH 4: SAFE COLLECTION ITERATION ==========
27
28 ArrayList<Integer> numbers = new ArrayList<>();
29 numbers.add(10);
30 numbers.add(null); // Null is added
31 numbers.add(20);
32
33 // Unsafe way (would crash):
34 // for (Integer num : numbers) {
35 // sum += num; // CRASH when num is null
36 // }
37
38 // Safe way - check each element:
39 int sum = 0;
40 for (Integer num : numbers) {
41 if (num != null) { // Check before using!
42 sum += num;
43 }
44 }
45
46 System.out.println("Sum (safe): " + sum); // Output: 30
47
48 // ========== APPROACH 5: REMOVE NULLS FROM COLLECTION ==========
49
50 ArrayList<Integer> moreNumbers = new ArrayList<>();
51 moreNumbers.add(10);
52 moreNumbers.add(null);
53 moreNumbers.add(20);
54
55 moreNumbers.removeIf(n -> n == null); // Remove all nulls!
56
57 // Now safe to use without checking
58 int sum2 = 0;
59 for (Integer num : moreNumbers) {
60 sum2 += num; // Safe - no nulls left
61 }
62
63 System.out.println("Sum (filtered): " + sum2); // Output: 30
64 }
65}What this program does:
Approach 1 (Lines 5-12):
- ›Always check
if (value != null)before using - ›Safest and most explicit approach
- ›Good for beginners
Approach 2 (Lines 16-18):
- ›Use ternary operator to provide default value
- ›Compact way to handle nulls
- ›Common in production code
Approach 3 (Lines 22-24):
- ›Use Java 8's Optional class
- ›More advanced but very clean
- ›Common in modern Java
Approach 4 (Lines 28-46):
- ›Check each element in loop
- ›Necessary when collection might contain nulls
- ›Safe but requires discipline
Approach 5 (Lines 50-63):
- ›Remove all nulls from collection first
- ›Then you don't need to check
- ›Clean and efficient
Output:
Value is null - cannot calculate
Final score: 0
Age: 18
Sum (safe): 30
Sum (filtered): 30
Best Practice:
- ›ALWAYS check for null when wrapper might be null
- ›Especially important when getting data from:
- ›Databases (fields can be NULL)
- ›User input (might be missing)
- ›APIs (might return null)
- ›Collections (might contain null)
Comparison: == vs equals()
Critical difference when using wrappers:
1public class ComparisonDemo {
2 public static void main(String[] args) {
3 // ========== CACHED VALUES (-128 to 127) ==========
4
5 Integer a = 100;
6 Integer b = 100;
7
8 System.out.println(a == b); // true (same cached object)
9 System.out.println(a.equals(b)); // true (same value)
10
11 // ========== NON-CACHED VALUES ==========
12
13 Integer c = 1000;
14 Integer d = 1000;
15
16 System.out.println(c == d); // false! (different objects)
17 System.out.println(c.equals(d)); // true (same value)
18
19 // ========== WHY THIS HAPPENS ==========
20
21 // Integer.valueOf() caches values from -128 to 127
22 Integer cached1 = Integer.valueOf(50);
23 Integer cached2 = Integer.valueOf(50);
24 System.out.println(cached1 == cached2); // true (same object)
25
26 Integer notCached1 = Integer.valueOf(200);
27 Integer notCached2 = Integer.valueOf(200);
28 System.out.println(notCached1 == notCached2); // false (different objects)
29
30 // ========== THE RULE ==========
31
32 // ✅ ALWAYS use .equals() for value comparison
33 Integer x = 500;
34 Integer y = 500;
35
36 if (x.equals(y)) { // CORRECT - compares values
37 System.out.println("Values are equal");
38 }
39
40 // ❌ DON'T use == for wrappers
41 if (x == y) { // WRONG - compares references
42 System.out.println("Won't print for large values");
43 }
44
45 // ========== MIXED COMPARISON ==========
46
47 Integer wrapper = 100;
48 int primitive = 100;
49
50 // Wrapper vs primitive - unboxing happens
51 System.out.println(wrapper == primitive); // true (wrapper unboxes)
52 // Compiler: wrapper.intValue() == primitive
53 }
54}Collections and Autoboxing
Collections heavily rely on autoboxing/unboxing:
1import java.util.*;
2
3public class CollectionsAutoboxing {
4 public static void main(String[] args) {
5 // ========== ARRAYLIST ==========
6
7 ArrayList<Integer> numbers = new ArrayList<>();
8
9 // Adding - autoboxing
10 numbers.add(10); // int → Integer
11 numbers.add(20);
12 numbers.add(30);
13
14 // Getting - unboxing
15 int first = numbers.get(0); // Integer → int
16 System.out.println("First: " + first); // 10
17
18 // Iterating
19 for (int num : numbers) { // Unboxing each element
20 System.out.print(num + " "); // 10 20 30
21 }
22 System.out.println();
23
24 // ========== HASHMAP ==========
25
26 HashMap<String, Integer> scores = new HashMap<>();
27
28 // Putting - autoboxing values
29 scores.put("Alice", 95); // int → Integer
30 scores.put("Bob", 87);
31 scores.put("Carol", 92);
32
33 // Getting - unboxing
34 int aliceScore = scores.get("Alice"); // Integer → int
35 System.out.println("Alice: " + aliceScore); // 95
36
37 // Updating - both autoboxing and unboxing
38 scores.put("Alice", scores.get("Alice") + 5);
39 // Steps:
40 // 1. get("Alice") returns Integer
41 // 2. Unbox to int
42 // 3. Add 5
43 // 4. Autobox result
44 // 5. Put back
45
46 // ========== SORTING ==========
47
48 List<Integer> nums = Arrays.asList(5, 2, 8, 1, 9);
49 Collections.sort(nums);
50
51 System.out.println("Sorted: " + nums); // [1, 2, 5, 8, 9]
52
53 // ========== STREAMING (Java 8+) ==========
54
55 List<Integer> values = Arrays.asList(1, 2, 3, 4, 5);
56
57 int sum = values.stream()
58 .mapToInt(i -> i) // Unboxing Integer → int
59 .sum();
60
61 System.out.println("Sum: " + sum); // 15
62
63 // Or using reduce (involves autoboxing/unboxing)
64 int total = values.stream()
65 .reduce(0, (a, b) -> a + b);
66
67 System.out.println("Total: " + total); // 15
68 }
69}Common Patterns and Use Cases
Pattern 1: Counting with HashMap
1import java.util.HashMap;
2
3public class CountingPattern {
4 public static void main(String[] args) {
5 String text = "hello world";
6 HashMap<Character, Integer> charCount = new HashMap<>();
7
8 for (char c : text.toCharArray()) {
9 if (c != ' ') {
10 // Get count (unbox), increment, put back (autobox)
11 charCount.put(c, charCount.getOrDefault(c, 0) + 1);
12 }
13 }
14
15 System.out.println(charCount);
16 // {d=1, e=1, h=1, l=3, o=2, r=1, w=1}
17 }
18}Pattern 2: Finding Max/Min in Collections
1import java.util.*;
2
3public class MaxMinPattern {
4 public static void main(String[] args) {
5 List<Integer> numbers = Arrays.asList(45, 23, 67, 12, 89, 34);
6
7 // Using Collections (handles autoboxing/unboxing)
8 int max = Collections.max(numbers); // Unboxing
9 int min = Collections.min(numbers); // Unboxing
10
11 System.out.println("Max: " + max); // 89
12 System.out.println("Min: " + min); // 12
13
14 // Manual approach
15 Integer maximum = numbers.get(0);
16 for (Integer num : numbers) { // Unboxing
17 if (num > maximum) { // Unboxing for comparison
18 maximum = num;
19 }
20 }
21
22 System.out.println("Maximum: " + maximum); // 89
23 }
24}Pattern 3: Accumulation
1import java.util.*;
2
3public class AccumulationPattern {
4 public static void main(String[] args) {
5 List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);
6
7 Integer total = 0; // Using wrapper
8 for (Integer price : prices) {
9 total += price; // Unbox both, add, autobox result
10 }
11
12 System.out.println("Total: $" + total); // $150
13
14 // Better performance with primitive
15 int total2 = 0;
16 for (Integer price : prices) { // Unbox once
17 total2 += price;
18 }
19
20 System.out.println("Total: $" + total2); // $150
21 }
22}Best Practices
✅ DO: Use Primitives for Arithmetic
1// ✅ GOOD
2int sum = 0;
3for (int i = 0; i < 1000; i++) {
4 sum += i;
5}
6Integer finalSum = sum; // Convert once at end
7
8// ❌ BAD
9Integer sum = 0;
10for (int i = 0; i < 1000; i++) {
11 sum += i; // Unbox, add, autobox - 1000 times!
12}✅ DO: Check for Null
1// ✅ GOOD
2Integer value = getUserInput();
3if (value != null) {
4 int result = value + 10;
5}
6
7// ❌ BAD
8Integer value = getUserInput();
9int result = value + 10; // NPE if value is null✅ DO: Use equals() for Comparison
1// ✅ GOOD
2Integer a = 1000;
3Integer b = 1000;
4if (a.equals(b)) { // true
5 System.out.println("Equal");
6}
7
8// ❌ BAD
9if (a == b) { // false (different objects)
10 System.out.println("Won't print");
11}✅ DO: Prefer valueOf() Over Constructor
1// ✅ GOOD
2Integer num = Integer.valueOf(10); // Uses cache
3
4// ❌ BAD (deprecated in Java 9+)
5Integer num = new Integer(10); // Always creates new object✅ DO: Be Aware of Performance
1// ✅ GOOD - Primitive array for performance
2int[] numbers = new int[1000];
3for (int i = 0; i < numbers.length; i++) {
4 numbers[i] = i * 2;
5}
6
7// ❌ BAD - Wrapper array has overhead
8Integer[] numbers = new Integer[1000];
9for (int i = 0; i < numbers.length; i++) {
10 numbers[i] = i * 2; // Autoboxing each time
11}Common Mistakes
Mistake 1: Assuming == Works for All Values
1// ❌ WRONG
2Integer a = 200;
3Integer b = 200;
4if (a == b) { // false! Different objects
5 System.out.println("Won't print");
6}
7
8// ✅ CORRECT
9if (a.equals(b)) { // true
10 System.out.println("Equal values");
11}Mistake 2: Not Checking Null
1// ❌ WRONG
2public int calculate(Integer value) {
3 return value * 2; // NPE if value is null
4}
5
6// ✅ CORRECT
7public int calculate(Integer value) {
8 if (value == null) {
9 return 0; // Or throw exception
10 }
11 return value * 2;
12}Mistake 3: Unnecessary Autoboxing in Loops
1// ❌ WRONG - Slow
2Integer sum = 0;
3for (int i = 0; i < 10000; i++) {
4 sum += i; // Unbox sum, add, autobox - every iteration
5}
6
7// ✅ CORRECT - Fast
8int sum = 0;
9for (int i = 0; i < 10000; i++) {
10 sum += i; // Primitive arithmetic
11}Mistake 4: Using Wrong Collection Type
1// ❌ WRONG - If you need primitives, using wrapper collection is wasteful
2ArrayList<Integer> largeDataset = new ArrayList<>();
3for (int i = 0; i < 1_000_000; i++) {
4 largeDataset.add(i); // 1 million autoboxing operations
5}
6
7// ✅ CORRECT - Use primitive array or specialized library
8int[] largeDataset = new int[1_000_000];
9for (int i = 0; i < 1_000_000; i++) {
10 largeDataset[i] = i; // Pure primitive
11}Practice Examples
Example 1: Grade Calculator
1import java.util.*;
2
3public class GradeCalculator {
4 public static void main(String[] args) {
5 List<Integer> scores = Arrays.asList(85, 92, 78, 95, 88);
6
7 // Calculate average (autoboxing/unboxing)
8 int sum = 0;
9 for (Integer score : scores) { // Unboxing
10 sum += score;
11 }
12
13 double average = (double) sum / scores.size();
14
15 // Find max (unboxing for comparison)
16 Integer max = Collections.max(scores);
17
18 // Count scores above average
19 int aboveAverage = 0;
20 for (Integer score : scores) {
21 if (score > average) { // Unboxing
22 aboveAverage++;
23 }
24 }
25
26 System.out.println("Scores: " + scores);
27 System.out.printf("Average: %.2f\n", average);
28 System.out.println("Highest: " + max);
29 System.out.println("Above average: " + aboveAverage);
30 }
31}Example 2: Word Frequency Counter
1import java.util.*;
2
3public class WordFrequency {
4 public static void main(String[] args) {
5 String text = "java is fun java is powerful java is everywhere";
6 String[] words = text.split(" ");
7
8 HashMap<String, Integer> frequency = new HashMap<>();
9
10 for (String word : words) {
11 // Autoboxing: int → Integer
12 frequency.put(word, frequency.getOrDefault(word, 0) + 1);
13 }
14
15 System.out.println("Word frequencies:");
16 for (Map.Entry<String, Integer> entry : frequency.entrySet()) {
17 System.out.println(entry.getKey() + ": " + entry.getValue());
18 }
19
20 // Output:
21 // everywhere: 1
22 // fun: 1
23 // is: 3
24 // java: 3
25 // powerful: 1
26 }
27}Example 3: Number Statistics
1import java.util.*;
2
3public class NumberStatistics {
4 public static void main(String[] args) {
5 List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
6
7 // Sum
8 Integer sum = 0;
9 for (Integer num : numbers) {
10 sum += num; // Unbox, add, autobox
11 }
12
13 // Average
14 double average = (double) sum / numbers.size();
15
16 // Variance
17 double variance = 0;
18 for (Integer num : numbers) {
19 double diff = num - average; // Unboxing
20 variance += diff * diff;
21 }
22 variance /= numbers.size();
23
24 // Standard deviation
25 double stdDev = Math.sqrt(variance);
26
27 System.out.println("Numbers: " + numbers);
28 System.out.println("Sum: " + sum);
29 System.out.printf("Average: %.2f\n", average);
30 System.out.printf("Std Dev: %.2f\n", stdDev);
31 }
32}Summary
Autoboxing:
- ›Automatic: primitive → wrapper
- ›Compiler calls
valueOf() - ›Happens: assignment, method calls, collections
- ›Convenient but has performance cost
Unboxing:
- ›Automatic: wrapper → primitive
- ›Compiler calls
xxxValue() - ›Happens: arithmetic, comparisons, assignments
- ›Can cause NullPointerException
Performance:
- ›10-20x slower than primitive arithmetic
- ›Avoid in loops and intensive calculations
- ›Use primitives for heavy computation
- ›Convert to wrapper at end if needed
Null Safety:
- ›Wrappers can be null, primitives cannot
- ›Always check null before unboxing
- ›Use Optional or default values
- ›Filter nulls from collections
Comparison:
- ›Never use
==for wrapper comparison - ›Always use
.equals()for values - ›
==compares references, not values - ›Caching only works for small values
Master autoboxing/unboxing and write safer, faster Java code! 🚀