Hi! You are already well acquainted with primitive types, and have worked quite a bit with them. In programming (and Java in particular), primitives have many advantages: they use little memory (and thus make the program more efficient) and have a clearly delineated range of values. However, while learning Java, we've already often repeated the mantra "everything in Java is an object". But primitives directly contradict those words. They aren't objects. So, is our "everything is an object" principle false? Actually, it's not. In Java, every primitive type has a twin brother, a wrapper class. Wrappers, unboxing, and boxing - 1 What's a wrapper? A wrapper is a special class that stores a primitive internally. But because it's a class, you can create instances of it. They store the primitive values internally, but are still real objects. Wrapper class names are very similar to (or exactly the same as) the names of their corresponding primitives. So, they are easy to remember.
Wrapper Classes for Primitive Data Types
Primitive Data Types Wrapper Classes
int Integer
short Short
long Long
byte Byte
float Float
double Double
char Character
boolean Boolean
Wrapper objects are created the same way as any other object:
public static void main(String[] args) {

   Integer i = new Integer(682);

   Double d = new Double(2.33);

   Boolean b = new Boolean(false);
}
Wrapper classes let us mitigate the shortcomings of primitive types. The most obvious is that primitives don't have methods. For example, they don't have a toString() method, so you can't, for instance, convert an int to a String. But the Integer wrapper class makes this easy.
public static void main(String[] args) {

   Integer i = new Integer(432);

   String s = i.toString();
}
However, converting in the other direction can be trickier. Let's say we have a String, which we know for sure contains a number. Regardless, there's no native way to use a primitive int to extract the number from the String and convert it to a number. But, we can with the wrapper classes.
public static void main(String[] args) {

   String s = "1166628";

   Integer i = Integer.parseInt(s);

   System.out.println(i);
}
Output: 1166628 We successfully extracted a number from the String and assigned it to the Integer reference variable i. By the way, regarding references. You already know that arguments are passed to methods in different ways: primitives by value, and objects by reference. You can use this knowledge when creating your own methods: for example, if your method uses fractional numbers but you need logic to pass by reference, you can pass Double/Float arguments to the method instead of double/float. In addition to wrapper classes' methods, their static fields can also be very convenient. For example, imagine you have the following task: display the maximum possible int value, followed by the minimum possible value. This problem seems rather basic. But without Google, it's unlikely you could do it. But wrappers allow you to easily handle such "mundane tasks":
public class Main {
   public static void main(String[] args) {

       System.out.println(Integer.MAX_VALUE);
       System.out.println(Integer.MIN_VALUE);
   }
}
These fields keep you from getting distracted from accomplishing more serious tasks. Not to mention the fact that typing in 2147483647 (which happens to be the value of MAX_VALUE) is no minor feat! :) Moreover, in a previous lesson, we pointed out that wrapper objects are immutable.
public static void main(String[] args) {

   Integer a = new Integer(0);
   Integer b = new Integer(0);

   b = a;
   a = 1;
   System.out.println(b);
}
Output: 0 The state of the object originally pointed to by a didn't change (because the value of b would have also changed). As with Strings, instead of changing the wrapper object's state, an entirely new object is created in memory. So, why did Java's creators ultimately decide to leave primitive types in the language? Since everything should be an object, and we've got wrapper classes that can express everything that primitives express, why not only keep the wrappers in the language and remove the primitives? The answer is simple: performance. Primitive types are called primitive because they lack many of the "heavyweight" features of objects. Yes, objects have many convenient methods, but you don't always need them. Sometimes, all you need is the number 33, or 2.62, or true/false. In situations where the advantages of objects don't matter and aren't required for the program to function, primitives are far better suited to the task.

Autoboxing/unboxing

In Java, a feature of primitives and their wrappers is autoboxing/unboxing. Wrappers, unboxing, and boxing - 2 Let's dig into this concept. As we've already learned, Java is an object-oriented language. That means that all programs written in Java are made of objects. Primitives are not objects. But even so, a wrapper variable can be assigned a primitive value. This process is called autoboxing. Similarly, a primitive variable can be assigned a wrapper object. This process is called unboxing. For example:
public class Main {
   public static void main(String[] args) {
       int x = 7;
       Integer y = 111;
       x = y; // Unboxing
       y = x * 123; // Autoboxing
   }
}
In line 5, we assign y, which is an Integer object, to the primitive x. As you can see, we don't have to take any additional steps: the compiler knows that int and Integer are, essentially, the same thing. That's unboxing. Something similar is happening with autoboxing on line 6: the primitive value (x * 123) is easily assigned to object y. This is an example of autoboxing. That's why the term includes the word "auto": because you don't have to do anything special to assign primitives to their corresponding wrapper objects (and vice versa). It all happens automatically. Convenient, huh? :) We see another example of the convenience of autoboxing/unboxing when working with methods. This is because method arguments are also autoboxed and unboxed. For example, if a method takes two Integer objects as inputs, we can easily pass ordinary ints instead!
public class Main {
   public static void main(String[] args) {

       printNumber(7);// A standard int, not even an int variable
   }

   public static void printNumber(Integer i) {
       System.out.println("You entered the number " + i);
   }
}
Output: You entered the number 7 It works in the other direction, too:
public class Main {
   public static void main(String[] args) {

       printNumber(new Integer(632));
   }

   public static void printNumber(int i) {
       System.out.println("You entered the number " + i);
   }
}
An important point you need to remember is this: autoboxing and unboxing don't work for arrays!
public class Main {
   public static void main(String[] args) {

       int[] i = {1,2,3,4,5};

       printArray(i);// Error, this won't compile!
   }

   public static void printArray(Integer[] arr) {
       System.out.println(Arrays.toString(arr));
   }
}
Attempting to pass an array of primitives to a method that takes an array of objects will result in a compilation error. In conclusion, let's briefly compare primitives and wrappers one more time. Primitives:
  • Have performance advantages
Wrappers:
  • Allow us to not violate the "everything is an object" principle, which means that numbers, characters, and boolean values don't violate this concept
  • Expand the possibilities for working with these values by providing convenient methods and fields
  • Are necessary when a method only works with objects