Java 中的基本数据类型与包装类型:拆箱与装箱
本文详细介绍了 Java 中基本数据类型与包装类型之间的区别,以及它们之间的转换过程——拆箱与装箱。通过理解这些概念,可以更好地利用 Java 的特性,编写更高效、更灵活的代码。希望本文能帮助你在 Java 编程的道路上更进一步。
在 Java 中,基本数据类型(如 int
、double
、boolean
等)和对应的包装类型(如 Integer
、Double
、Boolean
等)是两种不同的数据表示方式。基本数据类型是为了提高程序的运行效率而设计的,而包装类型则提供了更多的功能和灵活性。本文将详细探讨基本数据类型与包装类型之间的区别,以及它们之间的转换过程——拆箱与装箱。
Integer chenmo = new Integer(10); // 手动装箱
int wanger = chenmo.intValue(); // 手动拆箱
1 基本数据类型与包装类型的区别
-
包装类型可以为
null
,而基本数据类型不可以包装类型可以为
null
,这使得它们可以应用于 POJO(Plain Ordinary Java Object)中,而基本数据类型则不行。例如:class Writer { private Integer age; private String name; // Getter 和 Setter 方法 }
在这个例子中,
age
字段使用了Integer
包装类型,因此它可以为null
。如果使用基本数据类型int
,则无法表示null
值。 -
包装类型可用于泛型,而基本数据类型不可以
泛型不支持基本数据类型,因此在使用泛型时,必须使用包装类型。例如:
List<Integer> list = new ArrayList<>(); // 正确 List<int> list = new ArrayList<>(); // 编译错误
这是因为泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是
Object
类及其子类——基本数据类型是个例外。 -
基本数据类型比包装类型更高效
基本数据类型在栈中直接存储具体数值,而包装类型则存储堆中的引用。因此,基本数据类型在内存使用和性能上更高效。例如:
int a = 100; // 基本数据类型 Integer b = 100; // 包装类型
基本数据类型
int
直接存储数值100
,而Integer
对象则存储在堆中,并有一个引用指向它。 -
包装类型的值可以相同,但却不相等
两个包装类型的值可以相同,但在使用
==
进行比较时,比较的是对象的引用,而不是值。例如:Integer chenmo = new Integer(10); Integer wanger = new Integer(10); System.out.println(chenmo == wanger); // false System.out.println(chenmo.equals(wanger)); // true
在这个例子中,
chenmo
和wanger
的值都是10
,但它们是两个不同的对象,因此==
比较结果为false
。而equals
方法比较的是值,因此结果为true
。
2 自动装箱与自动拆箱
Java 1.5 引入了自动装箱(Autoboxing)和自动拆箱(Unboxing)的功能,使得基本数据类型和包装类型之间的转换更加方便。
- 自动装箱:将基本数据类型转换为对应的包装类型。
- 自动拆箱:将包装类型转换为对应的基本数据类型。
例如:
Integer chenmo = 10; // 自动装箱
int wanger = chenmo; // 自动拆箱
反编译后的代码如下:
Integer chenmo = Integer.valueOf(10);
int wanger = chenmo.intValue();
可以看到,自动装箱是通过 Integer.valueOf()
完成的,而自动拆箱是通过 Integer.intValue()
完成的。
3 IntegerCache 缓存机制
在自动装箱过程中,Java 使用了一个缓存机制来优化性能。对于 Integer
类型,Java 在 -128
到 127
之间的值进行了缓存。例如:
Integer c = 100;
Integer d = 100;
System.out.println(c == d); // true
c = 200;
d = 200;
System.out.println(c == d); // false
在第一个例子中,c
和 d
都指向缓存中的同一个 Integer
对象,因此 ==
比较结果为 true
。而在第二个例子中,200
不在缓存范围内,因此 c
和 d
是两个不同的对象,==
比较结果为 false
。
自动装箱是通过 Integer.valueOf()
完成的,我们来看看这个方法的源码。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以看到Integer
的缓存类IntegerCache
,来看一下 IntegerCache
的源码:
private static class IntegerCache {
// 缓存的最小值,默认为 -128
static final int low = -128;
// 缓存的最大值,默认为 127,但可以通过 JVM 参数配置
static final int high;
static final Integer cache[];
static {
// 默认情况下 high 值为 127
int h = 127;
// 通过系统属性获取用户可能配置的更高的缓存上限
// integerCacheHighPropValue 是一个字符串,代表配置的高值
int i = parseInt(integerCacheHighPropValue);
// 确保缓存的最高值至少为 127
i = Math.max(i, 127);
// 设置 high 的值,但不能超过 Integer.MAX_VALUE - (-low) - 1
h = Math.min(i, Integer.MAX_VALUE - (-low) - 1);
high = h;
// 初始化缓存数组,大小为 high - low + 1
cache = new Integer[(high - low) + 1];
// 填充缓存,从 low 开始,为每个值创建一个 Integer 对象
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// 断言确保 high 的值至少为 127,这是 Java 语言规范要求的
assert IntegerCache.high >= 127;
}
}
-128
到 127
之间的数会从 IntegerCache
中取,然后比较,所以第二段代码(100
在这个范围之内)的结果是 true
,而第三段代码(200
不在这个范围之内,所以 new
出来了两个 Integer
对象)的结果是 false
。
结论:当需要进行自动装箱时,如果数字在 -128
至 127
之间时,会直接使用缓存中的对象,而不是重新创建一个对象。
4 自动拆箱的注意事项
自动拆箱虽然方便,但也可能引发性能问题。例如:
long t1 = System.currentTimeMillis();
Long sum = 0L;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
在这个例子中,sum
被声明为 Long
类型,而 i
是 int
类型。每次执行 sum += i
时,都会进行自动拆箱和自动装箱操作,导致性能下降。如果将 sum
声明为 long
类型,性能会显著提升。
5 总结
本文详细介绍了 Java 中基本数据类型与包装类型之间的区别,以及它们之间的转换过程——拆箱与装箱。通过理解这些概念,可以更好地利用 Java 的特性,编写更高效、更灵活的代码。希望本文能帮助你在 Java 编程的道路上更进一步。
6 思维导图
7 参考链接

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)