基本数据类型

Java 中有 8 种基本数据类型,主要为:

  • 6 种数字类型:
    • 4 种整型:byteshortintlong
    • 2 种浮点型:floatdouble
  • 1 种字符类型:char
  • 1 种布尔型:boolean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * byte: -128 ~ 127
    * short: -32768 ~ 32767
    * int: -2147483648 ~ 2147483647
    * long: -9223372036854775808 ~ 9223372036854775807
    *
    * float: 1.4e-45 ~ 3.4028235e38
    * double: 4.9e-324 ~ 1.7976931348623157e308
    *
    * char: 0 ~ 65535
    *
    * boolean: ture or false
    */
    8 种基本数据类型的默认值和占用空间大小为:
基本类型 位数 字节 默认值
byte 8 1 0
short 16 2 0
int 32 4 0
long 64 8 0L
char 16 2 ‘u0000’
float 32 4 0f
double 64 8 0d
boolean 1 false

对于 boolean,官方文档未明确定义,它依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1 位,但是实际中会考虑计算机高效存储因素。

包装类

8 种基本数据类型所对应的包装类分别为:ByteShortIntegerLongFloatDoubleCharacterBoolean

包装类的缓存机制

  • Java 中的包装类使用缓存机制提升性能,如果创建数的大小超过了缓存的对应范围就会创建新的对象。
  • 本质上,缓存机制是在性能和资源之间的权衡。

整型

ByteShortIntegerLong四种包装类默认创建 -128 ~ 127 的相应类型的缓存数据。
Integer为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Integer缓存源码
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static {
// high value may be configured by property
int h = 127;
}
}

所有整型包装类对象值的比较,全部使用equals()进行比较。

1
2
3
4
5
6
7
8
9
10
11
12
Integer i1 = 143;
Integer i2 = 143;
Integer i3 = 12;
Integer i4 = 12;
Integer i5 = new Integer(12);

System.out.println(i1 == i2); // false
System.out.println(i1.equals(i2)); // true

System.out.println(i3 == i4); // true
System.out.println(i3 == i5); // false
System.out.println(i3.equals(i5)); // true

在 -128 ~ 127 之间赋值的Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断。但是这个区间之外的所有数据或者Integer对象,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals()进行判断。

字符型

Character创建了 0 ~ 127 范围的缓存数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Character 缓存源码
*/
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}

private static class CharacterCache {
private CharacterCache(){}
static final Character cache[] = new Character[127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Character((char)i);
}
}

布尔型

Boolean直接返回TrueFalse

1
2
3
4
5
6
/**
* Boolean 缓存源码
*/
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}

浮点型

特别地,Java 中的FloatDouble没有实现缓存机制。

和基本数据类型的区别

  • 包装类型可以用于泛型,基本类型不可以。
  • 相比对象类型,基本数据类型占用的空间非常小。
  • 不赋值的情况下,包装类型默认为 null,基本类型有默认值不为 null。
  • 基本数据类型的局部变量存放在 JVM 栈的局部变量表中,基本数据类型的成员变量(未被 static 修饰)存放在 JVM 的堆中,因为包装类型属于对象类型,几乎都存放在堆中。

装箱和拆箱

自动拆箱和装箱从 Java1.5 开始引入,目的是为了让原始类型和对应的包装类对象之间可以自动的相互转换。

实现原理

  • 装箱:调用了包装类的 valueOf 方法;
  • 拆箱:调用了包装类的 xxxValue 方法(xxx 代表对应的基本数据类型);

编译器会自动调用两个方法实现原始数据类型和包装类对象之间的转换。

1
2
3
4
// 装箱
Integer i = Integer.valueOf(10);
// 拆箱
int n = i.intValue();

触发时机

赋值

1
2
3
4
5
6
7
8
9
10
11
/**
* 赋值时触发拆箱或装箱
*/

//before autoboxing
Integer iObject = Integer.valueOf(3);
int iPrimitive = iObject.intValue()

//after java5
Integer iObject = 3; //autobxing - primitive to wrapper conversion
int iPrimitive = iObject; //unboxing - object to primitive conversion

方法调用

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 方法调用时触发拆箱和装箱
*/

public static Integer show(Integer iParam){
System.out.println("autoboxing example - method invocation i: " + iParam);
return iParam;
}

//autoboxing and unboxing in method invocation
show(3); //autoboxing
int result = show(3); //unboxing because return type of method is Integer

调用show(3)时,会将int自动装箱为Integer。使用show的返回值时,会自动将Integer拆箱为int

使用注意

循环中的自动装箱和拆箱

1
2
3
4
5
6
7
8
/**
* 循环中的自动装箱和拆箱
*/

Integer sum = 0;
for (int i = 0; i < 10000; i++) {
sum = sum + i;
}

上述的代码中,Integerint数据相加时,会发生自动拆箱和装箱。首先要对sum进行自动拆箱,数据相加结束后再进行自动装箱转换为Integer。流程可以概括为:

1
2
int result = sum.intValue() + i;
Integer sum = Integer.valueOf(result);

在上面的循环中,会频繁创建很多无用的Integer对象,提高垃圾回收的工作量和导致程序性能下降。所以在日常的使用中需要注意避免不必要的自动拆箱和装箱

包装类对象和基本数据类型的比较

当包装类进行算术运算或者与基本数据类型进行比较时,会自动拆箱或装箱。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 和基本数据类型的比较
*/

int i1 = 12;
Integer i2 = 12;
Integer i3 = 12;
Integer i4 = 24;

System.out.println(i1 == i2); // 和基本数据类比较,自动拆箱
System.out.println(i2.equals(i1)); // 和包装类比较,自动装箱
System.out.println(i4 == (i2 + i3)); // i2 + i3 算术运算自动拆箱,之后数值比较
System.out.println(i4.equals(i2 + i3)); // i2 + i3 算术运算自动拆箱,之后自动装箱

参考

  1. JavaGuide-基本数据类型和包装类
  2. Java 中的自动装箱和拆箱
  3. 深入剖析 Java 中的装箱和拆箱