基本数据类型和包装类型
两者的区别
- 用途:除了定义一些常量和局部变量之外,我们在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量。并且,包装类型可用于泛型,而基本类型不可以。
- 存储方式:基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被
static
修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。 - 占用空间:相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小。
- 默认值:成员变量包装类型不赋值就是
null
,而基本类型有默认值且不是null
。 - 比较方式:对于基本数据类型来说,
==
比较的是值。对于包装数据类型来说,==
比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用equals()
方法。
为什么说是几乎所有对象实例都存在于堆中呢? 这是因为 HotSpot 虚拟机引入了 JIT 优化之后,会对对象进行逃逸分析,如果发现某一个对象并没有逃逸到方法外部,那么就可能通过标量替换来实现栈上分配,而避免堆上分配内存
⚠️ 注意:基本数据类型存放在栈中是一个常见的误区! 基本数据类型的成员变量如果没有被 static
修饰的话(不建议这么使用,应该要使用基本数据类型对应的包装类型),就存放在堆中。
1 | class BasicTypeVar{ |
包装类的缓存机制
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False
。如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
Integer缓存源码
1 | public static Integer valueOf(int i) { |
两种浮点数类型的包装类 Float
,Double
并没有实现缓存机制。
1 | Integer i1 = 33; |
下面的代码的输出结果是 true
还是 false
呢?
1 | Integer i1 = 40; |
Integer i1=40
这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40)
。因此,i1
直接使用的是缓存中的对象。而Integer i2 = new Integer(40)
会直接创建新的对象。
因此,答案是 false
。
记住:所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
自动装箱和拆箱的原理
自动拆装箱
- 装箱:将基本类型用它们对应的引用类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
1 | Integer i = 10; //装箱 |
装箱其实就是调用了 包装类的valueOf()
方法,拆箱其实就是调用了 xxxValue()
方法。
因此,
Integer i = 10
等价于Integer i = Integer.valueOf(10)
int n = i
等价于int n = i.intValue()
;
注意:如果频繁拆装箱的话,也会严重影响系统的性能。我们应该尽量避免不必要的拆装箱操作。