I. Primitive types And Wrapper
1. Primitive types
In Java, it has 8 primitive types
byte
: 1 byte (8 bits), -128 ~ 127short
: 2 bytes, -2^15 ~ 2^15-1int
: 4 bytes, -2^31 ~ 2^31-1long
: 8 bytes, -2^63 ~ 2^63-1float
: 4 bytes, -3.403E38 ~ 3.403E38double
: 8 bytes, -1.798E308 ~ 1.798E308char
: 2 bytes, unicodeboolean
: 1 bytes, default value: false
Some notes:
byte
,short
,char
, these types, when they are used in calculation, all of them will cast toint
during the calculation. So after calculation, sometimes, we need to cast back to the original type.
short i = 1;
short j = 2;
short a = i + j;
// this line has problem, because (i + j ), it's int value, not a short.
// so the correct answer is
short a = (short)(i + j);
short j += i;
// this one is ok, because the operator like += , it will cast to original type automatically
- Conversion flow:
byte, short, char - int - long - float - double
, conversion is allowed from left to right byte
,short
, andchar
, they can not convert to each other, they will be convert toint
first
2. Wrapper types
Primitive type is not an object, but the wrapper can operate them as an object. So sometimes the program expected an object of integer number, like ArrayList<T>
for instance, we can not pass a primitive int
, it should pass an Integer
into ArrayList.
Primitive type | Wrapper type |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
int a = 1; // primitive type, value is 1
Integer a = new Integer(1); // a is a reference, points to an object, the value of this obejct is 1
3. Difference between primitive type and wrapper
we know that, when we pass an argument to a method, there is difference between primitive type value and object value:
- For primitive type value: it will pass a copy of value into method
- For object: it will pass the reference of object into method, in fact it pass a pointer
So when we pass a primitive type like int a
, in fact, the real value passed into method is a copy of a
, all the operations inside method won’t affect the value of a
;
package org.lovian.wrapper;
public class Demo1 {
public static void main(String[] args) {
int a = 1;
intAddOne(a);
System.out.println(a); // 1
}
public static void intAddOne(int a){
a = a + 1;
}
}
But if we pass an object, Student a
for instance, it just pass the reference a
into method, so in the method, it’s an object, so we could use the methods defined in Student. To see the following example:
we define a Student class:
package org.lovian.entity;
public class Student {
private int id;
private String name;
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And then we do some test here:
package org.lovian.wrapper;
import org.lovian.entity.Student;
public class Demo2 {
public static void main(String[] args) {
Student student = new Student(1, "lovian");
System.out.println(student.getName()); // lovian
changeStudentName(student); // pass the reference
System.out.println(student.getName()); // hahaha
}
public static void changeStudentName(Student student) {
student.setName("hahaha");
}
}
In the result, we will see that the name of student has been changed. The reason is when we passed the student to the method changeStudentName()
, in fact it pass the reference of object ‘student’. So Inside the method, student still points to the real object ‘student’. That’s why here the student name has changed.
Until to here, maybe you will think, in the code Demo1
, if we pass a Integer into method addOne()
, the value shoud be changed. Let’s see example, we update Demo1 as follow:
package org.lovian.wrapper;
public class Demo1 {
public static void main(String[] args) {
int a = 1;
Integer b = new Integer(1);
intAddOne(a);
intAddOne(b);
System.out.println(a); // 1
System.out.println(b); // 1
integerAddOne(a);
integerAddOne(b);
System.out.println(a); // 1
System.out.println(b); // 1
System.out.println(b.compareTo(a)); // 0
}
public static void intAddOne(int a){
a = a + 1;
}
public static void integerAddOne(Integer a){
a = a + 1;
}
}
You may have the questions:
- Why all the result is
1
? It should change becauseb
is aInteger
object, why it not change to2
? - Why
a
can pass intointAddOne()
and alsointegerAddOne()
? - Why
b
can pass intointAddOne()
and alsointegerAddOne()
?
The reason is Autoboxing
and Unboxing
.
II. Autoboxing and Unboxing
1. Autoboxing
When we define a Integer variable, we could define like this:
Integer i = new Integer(1);
But also we could define like this:
Integer i = 1;
The second way to define a Integer variable, we call it Autoboxing
. It will wrap the primitive type value 1
to a Integer object.
2.Unboxing
If we define a int variable like this:
Integer a = 10;
int b = a;
When assign a to b, it will give the value of object a to b. This is Auto Unboxing
.
During the calculation, it’s the same:
Integer i = 10;
System.out.println(i + 10); // Auto Unboxing i, then do calculation
Boolean foo = true;
System.out.println(foo && false); // Auto unboxing foo, then do the calculation
3.Essence of Autoboxing and Unboxing
In fact Autoboxing
and Unboxing
is a compiler sugar.
When we define
Integer number = 100;
Oracle JDK will compile it as
Integer localInteger = Integer.valueOf(100)
So here valueOf()
is the way to wrap a primitive type into an object. So the following example can pass the compilation, but it will report an error during the runtime:
package org.lovian.wrapper;
public class Demo3 {
public static void main(String[] args) {
Integer i = null;
int j = i;
j = 5;
System.out.println(j);
}
}
The error message:
Exception in thread "main" java.lang.NullPointerException
at org.lovian.wrapper.Demo3.main(Demo3.java:6)
It’s because during the compilation, it will be compiled to
Object localObject = null;
int i = localObject.intValue(); // here will report the null pointer exception
4. Cache in Integer
Let’s do the test as follow:
package org.lovian.wrapper;
public class Demo4 {
public static void main(String[] args) {
Integer i = 100;
Integer j = 100;
if(i == j)
System.out.println("i = j");
else
System.out.println("i != j");
Integer k = 200;
Integer l = 200;
if(k == l)
System.out.println("k = l");
else
System.out.println("k != l");
}
}
The result is
i = j
k != l
Why here i = j
, but k != l
?
That’s because the compiler will use Integer.valueOf()
to create Integer instance. The implementation as follow:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache.low
is -128
by default, IntegerCache.high
is 127
by default.
- For i and j: it will create instance
i = 100
first, then when create instance j, it will check that if 100 is in the cache or not. Because -128 < 100 < 127, it won’t create a new instance, it just return the instance of i. That’s means i and j reference to the same object, the value of this object is 100; Soi = j
- For k and l: because 200 is greater then 127, so it will create two different instances for k and l, so
k != l
Share this on