# 基本数据类型

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

  • 6 种数字类型:
    • 4 种整型:byte、short、int、long
    • 2 种浮点型:float、double
  • 1 种字符类型:char
  • 1 种布尔型:boolean

8 种基本数据类型的默认值和占用空间大小为:

基本类型 位数 字节 默认值 取值范围
byte 8 1 0 -128 ~ 127
short 16 2 0 -32768 ~ 32767
int 32 4 0 -2147483648 ~ 2147483647
long 64 8 0L -9223372036854775808 ~ 9223372036854775807
char 16 2 ‘u0000’ 0 ~ 65535
float 32 4 0f 1.4E-45 ~ 3.4028235E38
double 64 8 0d 4.9E-324 ~ 1.7976931348623157E308
boolean 1 false ture、false

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

# 包装类

8 种基本数据类型所对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean。

| 包装类的缓存机制

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

* 整型

Byte、Short、Integer、Long 四种包装类默认创建 [-128 ~ 127] 的相应类型的缓存数据。
以 Integer 为例:

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

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

  1. Integer i1 = 143;
  2. Integer i2 = 143;
  3. Integer i3 = 12;
  4. Integer i4 = 12;
  5. Integer i5 = new Integer(12);
  6. System.out.println(i1 == i2); // false
  7. System.out.println(i1.equals(i2)); // true
  8. System.out.println(i3 == i4); // true
  9. System.out.println(i3 == i5); // false
  10. System.out.println(i3.equals(i5)); // true

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

* 字符型

Character 创建了 [0, 127] 范围的缓存数据。

  1. public static Character valueOf(char c) {
  2. if (c <= 127) { // must cache
  3. return CharacterCache.cache[(int)c];
  4. }
  5. return new Character(c);
  6. }
  7. private static class CharacterCache {
  8. private CharacterCache(){}
  9. static final Character cache[] = new Character[127 + 1];
  10. static {
  11. for (int i = 0; i < cache.length; i++)
  12. cache[i] = new Character((char)i);
  13. }
  14. }

* 布尔型

Boolean 直接返回 True 或 False。

  1. public static Boolean valueOf(boolean b) {
  2. return (b ? TRUE : FALSE);
  3. }

* 浮点型

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

| 和基本数据类型的区别

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

    # 装箱和拆箱

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

    | 实现原理

  • 装箱:调用了包装类的 valueOf 方法;

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

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

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

| 触发时机

* 赋值

  1. //before autoboxing
  2. Integer iObject = Integer.valueOf(3);
  3. int iPrimitive = iObject.intValue()
  4. //after java5
  5. Integer iObject = 3; //autobxing - primitive to wrapper conversion
  6. int iPrimitive = iObject; //unboxing - object to primitive conversion

* 方法调用

  1. public static Integer show(Integer iParam){
  2. System.out.println("autoboxing example - method invocation i: " + iParam);
  3. return iParam;
  4. }
  5. //autoboxing and unboxing in method invocation
  6. show(3); //autoboxing
  7. int result = show(3); //unboxing because return type of method is Integer

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

| 使用注意

* 循环中的自动装箱和拆箱

  1. Integer sum = 0;
  2. for (int i = 0; i < 10000; i++) {
  3. sum = sum + i;
  4. }

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

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

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

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

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

  1. int i1 = 12;
  2. Integer i2 = 12;
  3. Integer i3 = 12;
  4. Integer i4 = 24;
  5. System.out.println(i1 == i2); // 和基本数据类比较,自动拆箱
  6. System.out.println(i2.equals(i1)); // 和包装类比较,自动装箱
  7. System.out.println(i4 == (i2 + i3)); // i2 + i3 算术运算自动拆箱,之后数值比较
  8. System.out.println(i4.equals(i2 + i3)); // i2 + i3 算术运算自动拆箱,之后自动装箱

# 参考

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