数据类型
数据类型的分类
Java 数据类型分为两大类:原始类型(Primitive Types)和引用类型(Reference Types)。
原始类型 (8 种)
基本数据类型直接存储就是数据本身,内存分配在栈上,占用固定内存,效率高。
- 整型:用于存储没有小数部分的数字。
byte:占用 1 个字节(8 位),取值范围是 ,默认值是0,例如byte b = 10;。short:占用 2 个字节(16 位),取值范围是 ,默认值是0,例如short s = 1000;。int:是字面量默认的整数类型,占用 4 个字节(32 位),取值范围是 (约 亿),默认值是0,例如int i = 100000;。long:占用 8 个字节(64 位),取值范围是 ,默认值是0L,定义时需要加上L或l后缀,例如long l = 123456789L;。
- 浮点型:用于存储具有小数部分的数字。
float:占用 4 个字节(32 位),默认值是0.0f,定义时需要加上F或f后缀,例如float f = 3.14f;。double:是字面量默认的浮点类型,占用 8 个字节(64 位),默认值是0.0,例如double d = 3.14;。
- 字符型:用于存储单个字符,表示单个 Unicode 字符,比如
'A'对应 Unicode 值 65。char:占用 2 个字节(16 位),取值范围是 ,默认值是'\u0000',例如char c = 'A';。
- 布尔型:用于存储真(true)或假(false)。
boolean:单独使用时占用 1 个字节,使用数组存储时占用 1 位,仅有true和false两个值,且不能与其他类型相互转化,默认值是false,例如boolean flag = true;。
补充说明
// dubbole 的前导 0 可省略
double a = .1;
// 一个字符变量只能存单个字符,否则编译会报错
char c = 'ab';
// char 类型没有空字符,否则编译会报错
char c = '';
引用类型
引用类型存储的是数据的内存地址,而非实际的值,内存分配在堆上。
类(包括字符串、抽象类、枚举类、注解类、自定义类)、接口、数组都是引用类型,默认值是 null,例如:
// String 虽为引用类型,但也支持字面量直接赋值
String str = "Hello";
// 接口是引用类型
List<String> list = new ArrayList<>();
// 数组是引用类型
int[] arr = {1, 2, 3};
原始类型和引用类型的主要区别
- 存储内容:原始类型存储的就是数据本身。引用类型存储的是数据的内存地址,而非实际的值。
- 内存位置:原始类型内存分配在栈上。引用类型内存分配在堆上。
- 默认值:原始类型有默认值,比如
int的默认值为0,char的默认值为'\u0000'。引用类型的默认值是null,准确的来说null不属于默认值,而是代表没有指向任何内存地址。 - 比较操作:原始类型使用
==比较的是值。引用类型使用==比较的是内存地址,使用equals()才是比较的内容。
整数的不同进制表示方式
整数载源码层面还可以使用多种表示方式,比如:
- 十进制(Decimal):最常见的表示方式,比如
int a = 123;。 - 十六进制(Hexadecimal):以
0x或0X开头,比如int b = 0x7B;表示十进制 123。 - 八进制(Octal):以
0开头,比如int c = 0173;表示十进制 123。 - 二进制(Binary):Java 7 及以上支持,以
0b或0B开头,比如int d = 0b1111011;代表十进制 123。
浮点数的科学记数法表示方式
浮点数在源码层面还可以用科学计数法来表示,比如:
float f = 1.23e3f;表示 ,也就是 1230.0float f = 123e-3f;表示 ,也就是 0.123
🔔 记忆口诀:
E 前 E 后必有数,E 后必须为整数。
十进制数转换为二进制的数学运算
- 整数部分采用除 2 取余,逆序排列”的方法。
- 除 2 取余:将十进制整数除以 2,记录下商和余数,将得到的商继续除以 2,重复记录余数,直到商为 0。
- 逆序排列:将所有记录的余数逆序排列,就得到了该十进制整数对应的二进制数。
- 小数部分采用乘 2 取整,正序排列的方法。
- 乘 2 取整:将十进制小数乘以 2,记录下整数部分和小数部分,将得到的小数部分继续乘以 2 ,重复记录整数部分,直到小数部分为 0。
- 正序排列:将所有记录的整数部分正序排列,就得到了该十进制小数对应的二进制数。
- 最后合并整数部分和小数部分的二进制结果。
例如:将十进制数 9.625 转换为二进制时,需要分别处理整数部分(9)和小数部分(0.625)
- 整数部分转换:对于整数部分 9,使用“除 2 取余,逆序排列”的方法进行转换:
- 余数为
- 余数为
- 余数为
- 余数为
- 按照余数逆序排列,得到整数部分 9 的二进制是 。
- 小数部分转换:对于小数部分 0.625,使用“乘 2 取整法,正序排列”的方法进行转换:
- 取整部分为
- 取整部分为
- 取整部分为
- 按照取整部分顺序排列,得到小数部分 0.625 的二进制是 。
- 合并结果:将整数部分和小数部分合并,就得到了 的二进制表示
综上所述,十进制数 转换为二进制为 。
二进制数转换为十进制的数学运算
对二进制数的每一位的权值进行加权求和。
- 整数部分:从右到左,每个二进制位的位置按照从 0 开始的索引编号,首位的权值是 2 的 0 次方,第二位是 2 的 1 次方,依此类推,n 位的权值是 2 的 n 次方。
- 小数部分:从左到右,每个二进制位的位置按照从 -1 开始的索引编号,首位的权值是 2 的 -1 次方,第二位是 2 的 -2 次方,以此类推,n 位的权值是 2 的 -n 次方。
例如:将二进制数 转换为十进制时,将二进制数中每个 1 的位置的 2 的幂次方相加
- 整数部分:
- 小数部分:
- 将以上结果相加:
最终结果为:
整数的存储格式
整型(byte, short, int, long)使用补码(Two's Complement)方式进行存储。
- 其中最高位表示符号位:
0表示正数,1表示负数。 - 正数:直接使用其二进制原码作为补码。例如
10的原码和补码都是00001010。 - 负数:需要先取绝对值的得到其二进制原码,再按位取反得到反码,最后加 1 得到补码。例如
-10的补码就是通过,先取绝对值得到10的原码00001010,再按位取反得到10的反码11110101,最后加 1 得到 10 的补码11110110。
正是因为这种存储方式,所以:
- 负数的范围才会比正数多 1 个值。
- 0 才是唯一的,不会出现 的情况。
- 如果溢出,正数就会变成负数,负数就会变成正数,比如
2147483647 + 1就会等于-2147483648(最小值)。
浮点数的存储格式
Java 的浮点型(float, double)遵循 IEEE 754 标准。
float(32 位):1 位符号位,8 位指数位,23 位尾数位,偏移量为 127。double(64 位):1 位符号位,11 位指数位,52 位尾数位,偏移量为 1023。
例如:float 存储十进制数 9.625。
将其转化为二进制,并规范化为科学计数法:
将其分解为三部分:
- 符号位():正数取 。
- 指数位():取 ,由于
float的偏移量()为 ,所以实际的指数位为 。 - 尾数位():隐含掉 开头,所以取 ,向右侧补零到 23 位,得到 。
所以完整的 32 位存储就是 0_10000010_00110100000000000000000。
将二进制浮点数还原为十进制的数学运算
公式为:
其中: 表示符号位, 表示尾数位, 表示指数位, 表示偏移量。
例如:将二进制 0_10000010_00110100000000000000000 还原为十进制。
将其分解为三部分:
- 符号位():是 ,带入公式的第一部分 。
- 指数位():是 ,将其转化为十进制 ,由于
float的偏移量()为 ,所以将其带入公式的第三部分 。 - 尾数位():是 ,带入公式的第二部分 ,从而补回隐含的 。
最终计算结果为:
浮点数中的特殊值
由于浮点数的特殊存储格式,所以浮点数中存在一些特殊值,比如:
| 表示值 | 指数位 | 尾数位 |
|---|---|---|
| 全 0 | 全 0 | |
| 全 1 | 全 0 | |
| 全 1 | 非全 0 |
例如:
// 1.0 除以 0.0 会输出 NaN
double nan = 0.0 / 0.0;
// 1.0 除以 0.0 会输出 Infinity
double infinity = 1.0 / 0.0;
浮点精度问题
在计算机中,浮点数实际上是二进制的科学计数法表示,这种表示方式导致部分数值只能近似存储,因为某些十进制小数无法精确转换为二进制。例如,十进制 在二进制中实际上是无限循环小数 ,就像 在十进制中表示为 一样。
无论是 32 位的 float 还是 64 位的 double,它们的存储空间都是有限的,必须对无限小数进行截断或舍入。在连续运算过程中,这些微小的舍入误差会不断累积,最终可能导致结果有明显的偏差,比如:
// 0.1 + 0.2 实际上会输出 0.30000000000000004
dubbo sum = 0.1 + 0.2;
// 4.015 * 100 实际上会输出 401.49999999999994
dubbo product = 4.015 * 100;
综上所述,float 和 double 不适合精确计算(如货币),建议用 BigDecimal。

Comments NOTHING