Java 有两种有效的数据类型:
- 基本 (Primitive) 数据类型(也称为原始数据类型)
- 引用数据类型:封装类 (Warpper) 的引用
一、8 种基本数据类型
基本类型 | 名称 | 封装类 | 字节数 | 最大值 | 最小值 | 缓存范围 |
---|---|---|---|---|---|---|
byte | 字节型 | Byte | 1byte | 127 | -128 | -128~127 |
short | 短整型 | Short | 2byte | 2^15 - 1 | -2^15 | -128~127 |
int | 整型 | Integer | 4byte | 2^31 - 1 | -2^31 | -128~127 |
long | 长整型 | Long | 8byte | 2^63 - 1 | -2^63 | -128~127 |
float | 单精度浮点型 | Float | 4byte | 无缓存 | ||
double | 双精度浮点型 | Double | 8byte | 无缓存 | ||
char | 字符型 | Character | 2byte | \uffff 65,535 |
0 | 0~127 |
boolean | 布尔型 | Boolean | 不明确 | - | - | false、true |
byte
1byte 为 8bits,常用于将 Object 转换为 byte[] 数组,用于字节流 ByteArrayInputStream 的输入
char
2 个字节,可表示 65,536 个字符。最初设计能保存所有的 Unicode 字符,但现在 Unicode 包含的字符越来越多,已经收录超过 13 万个字符了(截止版本 12.0.0)。
\uffff
一个 16 进制可以表示 4 位。4 * 4 可表示 16 位,2 个字节。
Character c = '颜'; |
超过两个字节的中文字符,如:𦡦
𦡦 U+26866, 𦡦 |
超过 2 个字节的处理方式,用 String
更多:https://www.oracle.com/technical-resources/articles/javase/supplementary.html
boolean
Java 虚拟机规范 没有明确规定 boolean 的字节数,可能为 1 个字节,也可能为 4 个字节。
Java 编程语言中对布尔值进行操作的表达式被编译为使用 Java 虚拟机 int 数据类型的值。
在 Oracle 的 Java 虚拟机实现中,Java 编程语言中的布尔数组被编码为 Java 虚拟机字节数组,每个布尔元素使用 8 位。
float
IEEE 754-1985 将 4 个字节定义为单精度(single),浮点(浮动的小数点)
- 第 1 位表示正负,中间 8 位表示指数,后 23 位储存有效数位(有效数位是 24 位)。最左边 1 位表示 1
sign = 0 |
浮点型和二进制之间的相互转换:
float f = 0.15625f; |
浮点型转二进制过程:
// 示例:0.15625 |
我们可以看出,float 采用无限逼近的方式来表示小数。
最多保留 8 位小数?
≈ 1.9999999 |
浮点型,通过不断接近的方式来得到目标值
float 类型加:
1、5.01 + 4.21
public static void main(String[] args) { |
9.22 |
1.2525 1.01000000101000111101100 101000000101000111101100 |
2^2 * 1.2525 + 2^2 * 1.0525 = 2^2 * (1.2525 + 1.0525) = 2^2 * 2 * 1.1525 = 2^3 * 1.1525 |
0 10000001 01000000101000111101100 = 5.01f |
2、4.5 + 58.0
public static void main(String[] args) { |
62.5 |
2^2 * 1.0010 = 2^5 * 2^-3 * 1.0010 = 2^5 * 0.0010010 // 正数左移变大,小数左移变小 |
1.1101000 11101000 |
- 单精度浮点数 - Wiki
- 《编码 - 隐匿在计算机软硬件背后的语言》
double
double 和二进制之间的相互转换:
double d = 0.15625; |
double 类型加乘:
public static void main(String[] args) { |
9.219999999999999 |
直接使用 double 加乘,有精度损失的问题,此时使用可使用 BigDecimal。
class DoubleMathUtil { |
最多保留 17 位小数?0.18749999999999997
0.18749999999999997 |
int
int 转 2 进制。
int i = 560067; |
Integer i = 150; |
char 类型数字转 int
int i = '8' - '0' // 8 |
float 取整
Float a = 5.2f; |
- Byte 和 Boolean 类型缓存了全部值,缓存值存放在数组中。
- 封装类被 final 修饰,基本数据类型也算是 final 修饰。
二、基本数据类型与封装类的联系
使用集合时,其泛型必须为对象。
JDK5.0 开始提供自动封箱功能,基本数据类型可以自动封装成封装类。比如集合 List,往里添加对象 Object,JDK5.0 以前需要将数字封装成封装类型对象,再存到 List 中。
List list = new ArrayList(); |
在 JDK5.0 以后可以自动封箱,简写成:
List list = new ArrayList(); |
也可以自动拆箱,将封装器类型自动转换为对应的基本数据类型:
Integer a = 1;// 装箱,底层实现:Integer a = Integer.valueOf(1); |
ASM Bytecode Outline
反编译结果:
三、基本数据类型与其封装类的区别
1、默认值不同
int 是基本类型,直接存放数值;Integer 是类,产生对象时用一个引用指向这个对象。基本类型跟封装类型的默认值是不一样的。如 int i,i 的预设为 0;Integer j,j 的预设为 null,因为封装类产生的是对象,对象默认值为 null。
int i = 0; |
2、存储位置不同
基本类型在内存中是存储在 Java 虚拟机栈中,封装类的引用(值的地址)存储在 Java 虚拟机栈中,而实际的对象(值)是存在堆中。
3、作用不同
基本数据类型的好处就是速度快(在栈上分配内存效率高,且不涉及到对象的构造和回收),封装类的目的主要是更好的处理数据之间的转换(利用其方法和属性)。
四、应用场景
基本数据类型
- 性能要求高的场景
- 临时变量
封装类
- 集合类
- 泛型
- 对象序列化
- 使用 null 值。如前端接口请求参数、方法参数
封装类作为参数,参数可以为 null:
public void setTimeout(Integer timeout) { |
五、需注意
要注意自动拆箱出现空指针异常:
List<Integer> list = new ArrayList<Integer>(3); |
Exception in thread "main" java.lang.NullPointerException |
当方法重载时,考虑 Java 向前兼容,不自动装箱,而精准匹配。
public class BugInOverloading{ |
long num |