Java对比项(3) 基本数据类型&包装类
基本数据类型 & 包装类
Author: Corissa 👵
Date: Dec.29 2023
重点需要掌握🏁:
理解 自动装箱/拆箱
包装类的缓存机制
为什么需要基本数据类型
1 基本数据类型和包装类的基本回顾
基本数据类型 8 种
byte (1 byte), char (2 byte), short (2 byte), int (4 byte), long (8 byte), float (4 byte), double (8 byte), boolean
对于
boolean,官方文档未明确定义,依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1 位,但是实际中会考虑计算机高效存储因素。8 种基本数据类型都有对应的包装类
Byte, Character, Short, Integer, Long, Float, Double, Boolean
基本数据类型 vs 包装类型
用途
- 包装类型可以用于泛型,而基本类型不可以
- 基本数据类型的使用场景比较局限,容器存储时也不能存储基本数据类型。
存储方式
- 基本数据类型的局部变量存储在 Java 虚拟机栈的局部变量表中,成员变量(未被
statics修饰)存放在堆中。 - 包装类型的实例存放在堆中。
- 基本数据类型的局部变量存储在 Java 虚拟机栈的局部变量表中,成员变量(未被
占用空间
- 基本数据类型占用空间远小于包装类型,因为对象在内存中存储不仅包含实例数据,还包含对象头和对其填充,以下是《深入理解JAVA虚拟机的描述》:
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称它为”Mark Word”。
对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据信息并不一定要经过对象本身,这点将在2.3.3节讨论。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中却无法确定数组的大小。
接下来的实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。
第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。
比较方式
- 对于基本数据类型来说 “==” 比较的是值
- 对于包装类型来说 “==” 比较的是对象内存地址,equals() 比较的是值
2 包装类型的缓存机制
Java 基本数据类型的包装类型大部分都用到了缓存机制来提升性能。
Java 官方对 IntegerCache 描述如下:
Cache to support the object identity semantics of autoboxing for values between -128 and 127 (inclusive) as required by JLS.
以 Integer 为例,在自动装箱时,如果值大小在 -128~127 之间则从使用缓存中的对象:
1 | public static Integer valueOf(int i) { |
比如说:
1 | Integer i1 = 20; |
这里 i1 是自动装箱,会直接使用缓存中的20,i2 是创建新的对象,因此内存地址不同,打印 false
对于 Byte,Short,Integer,Long 这 4 种包装类,JVM 默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False。
对于包装类 Float,Double 并没有实现缓存机制。
3 自动装箱(boxing) & 自动拆箱(unboxing)
- boxing: 将基本数据类型用对应的引用包装起来,通过调用包装类的
valueOf()方法实现 - unboxing: 将包装类型转换为基本数据类型, 通过调用包装类的
xxxValue()方法如intValue()实现
原则上,尽量避免无意义的拆箱、装箱行为,会影响系统性能
4 其他
包装类的
value也被声明为不可变类型:private final int value;基本数据类型线程安全问题
基本数据类型的变量,显然要使用并发相关手段,才能保证线程安全。
比如可以使用类似
AtomicInteger、AtomicLong这样的线程安全类。特别的是,部分比较宽的数据类型,比如 float、double,可能甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值


