07-数据类型

nobility 发布于 2025-07-10 01-Java语言基础 676 次阅读


数据类型

数据类型的分类

Java 数据类型分为两大类:‌原始类型(Primitive Types)和‌引用类型(Reference Types)。

原始类型 (8 种)

基本数据类型直接存储就是数据本身,内存分配在栈上,占用固定内存,效率高。

  • 整型:用于存储没有小数部分的数字。
    • byte:占用 1 个字节(8 位),取值范围是 128127-128 \sim 127,默认值是 0,例如 byte b = 10;
    • short:占用 2 个字节(16 位),取值范围是 3276832767-32768 \sim 32767,默认值是 0,例如 short s = 1000;
    • int:是字面量默认的整数类型,占用 4 个字节(32 位),取值范围是 2312311-2^{31} \sim 2^{31} - 1(约 ±21\pm 21 亿),默认值是 0,例如 int i = 100000;
    • long:占用 8 个字节(64 位),取值范围是 2632631-2^{63} \sim 2^{63} - 1,默认值是 0L,定义时需要加上 Ll 后缀,例如 long l = 123456789L;
  • 浮点型:用于存储具有小数部分的数字。
    • float:占用 4 个字节(32 位),默认值是 0.0f,定义时需要加上 Ff 后缀,例如 float f = 3.14f;
    • double:是字面量默认的浮点类型,占用 8 个字节(64 位),默认值是 0.0,例如 double d = 3.14;
  • 字符型:用于存储单个字符,表示单个 Unicode 字符,比如 'A' 对应 Unicode 值 65。
    • char:占用 2 个字节(16 位),取值范围是 0655350 \sim 65535,默认值是 '\u0000',例如 char c = 'A';
  • 布尔型:用于存储真(true)或假(false)。
    • boolean:单独使用时占用 1 个字节,使用数组存储时占用 1 位,仅有 truefalse 两个值,且不能与其他类型相互转化,默认值是 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 的默认值为 0char 的默认值为 '\u0000'。引用类型的默认值是 null,准确的来说 null 不属于默认值,而是代表没有指向任何内存地址。
  • 比较操作:原始类型使用 == 比较的是值。引用类型使用 == 比较的是内存地址,使用 equals() 才是比较的内容。

整数的不同进制表示方式

整数载源码层面还可以使用多种表示方式,比如:

  • 十进制(Decimal):最常见的表示方式,比如 int a = 123;
  • 十六进制(Hexadecimal):以 0x0X 开头,比如 int b = 0x7B; 表示十进制 123。
  • 八进制(Octal):以 0 开头,比如 int c = 0173; 表示十进制 123。
  • 二进制(Binary):Java 7 及以上支持,以 0b0B 开头,比如 int d = 0b1111011; 代表十进制 123。

浮点数的科学记数法表示方式

浮点数在源码层面还可以用科学计数法来表示,比如:

  • float f = 1.23e3f; 表示 1.23×1031.23 \times 10^3,也就是 1230.0
  • float f = 123e-3f; 表示 123×103123 \times 10^{-3},也就是 0.123

🔔 记忆口诀:

E 前 E 后必有数,E 后必须为整数。

十进制数转换为二进制的数学运算

  • 整数部分采用除 2 取余,逆序排列”的方法。
    • 除 2 取余:将十进制整数除以 2,记录下商和余数,将得到的商继续除以 2,重复记录余数,直到商为 0。
    • 逆序排列:将所有记录的余数逆序排列,就得到了该十进制整数对应的二进制数。
  • 小数部分采用乘 2 取整,正序排列的方法。
    • 乘 2 取整:将十进制小数乘以 2,记录下整数部分和小数部分,将得到的小数部分继续乘以 2 ,重复记录整数部分,直到小数部分为 0。
    • 正序排列:将所有记录的整数部分正序排列,就得到了该十进制小数对应的二进制数。
  • 最后合并整数部分和小数部分的二进制结果。

例如:将十进制数 9.625 转换为二进制时,需要分别处理整数部分(9)和小数部分(0.625)

  • 整数部分转换:对于整数部分 9,使用“除 2 取余,逆序排列”的方法进行转换:
    • 9÷2=49 \div 2 = 4 余数为 11
    • 4÷2=24 \div 2 = 2 余数为 00
    • 2÷2=12 \div 2 = 1 余数为 00
    • 1÷2=01 \div 2 = 0 余数为 11
    • 按照余数逆序排列,得到整数部分 9 的二进制是 10011001
  • 小数部分转换:对于小数部分 0.625,使用“乘 2 取整法,正序排列”的方法进行转换:
    • 0.625×2=1.250.625 \times 2 = 1.25 取整部分为 11
    • 0.25×2=0.50.25 \times 2 = 0.5 取整部分为 00
    • 0.5×2=1.00.5 \times 2 = 1.0 取整部分为 11
    • 按照取整部分顺序排列,得到小数部分 0.625 的二进制是 101101
  • 合并结果:将整数部分和小数部分合并,就得到了 9.6259.625 的二进制表示 9.62510=1001.10129.625_{10} = 1001.101_2

综上所述,十进制数 9.6259.625 转换为二进制为 1001.1011001.101

二进制数转换为十进制的数学运算

对二进制数的每一位的权值进行加权求和。

  • 整数部分:从右到左,每个二进制位的位置按照从 0 开始的索引编号,首位的权值是 2 的 0 次方,第二位是 2 的 1 次方,依此类推,n 位的权值是 2 的 n 次方。
  • 小数部分:从左到右,每个二进制位的位置按照从 -1 开始的索引编号,首位的权值是 2 的 -1 次方,第二位是 2 的 -2 次方,以此类推,n 位的权值是 2 的 -n 次方。

例如:将二进制数 1001.1011001.101 转换为十进制时,将二进制数中每个 1 的位置的 2 的幂次方相加

  • 整数部分:
    • 1×23=81 \times 2^3 = 8
    • 0×22=00 \times 2^2 = 0
    • 0×21=00 \times 2^1 = 0
    • 1×20=11 \times 2^0 = 1
  • 小数部分:
    • 1×21=0.51 \times 2^{-1} = 0.5
    • 0×22=00 \times 2^{-2} = 0
    • 1×23=01251 \times 2^{-3} = 0。125
  • 将以上结果相加:
    • 8+0+0+1=98 + 0 + 0 + 1 = 9
    • 0.5+0+0.125=0.6250.5 + 0 + 0.125 = 0.625

最终结果为:9+0.625=9.6259 + 0.625 = 9.625

整数的存储格式

整型(byteshortintlong)使用‌补码(Two's Complement)‌方式进行存储。

  • 其中最高位表示符号位:0 表示正数,1 表示负数。
  • 正数:直接使用其二进制原码‌作为补码。例如 10 的原码和补码都是 00001010
  • 负数:需要先取绝对值的得到其二进制原码,再按位取反得到反码,最后加 1 得到补码。例如 -10 的补码就是通过,先取绝对值得到 10 的原码 00001010,再按位取反得到 10 的反码 11110101,最后加 1 得到 10 的补码 11110110

正是因为这种存储方式,所以:

  • 负数的范围才会比正数多 1 个值。
  • 0 才是唯一的,不会出现 ±0\pm 0 的情况。
  • 如果溢出,正数就会变成负数,负数就会变成正数,比如 2147483647 + 1 就会等于 -2147483648(最小值)。

浮点数的存储格式

Java 的浮点型(floatdouble)遵循‌ IEEE 754 标准‌。

  • float(32 位):1 位符号位,8 位指数位,23 位尾数位,偏移量为 127。
  • double(64 位):1 位符号位,11 位指数位,52 位尾数位,偏移量为 1023。

例如float 存储十进制数 9.625

将其转化为二进制,并规范化为科学计数法:

9.62510=1001.1012=1.0011012×239.625_{10} = 1001.101_2 = 1.001101_2 \times 2^3

将其分解为三部分:

  • 符号位(signsign):正数取 00
  • 指数位(exponentexponent):取 33,由于 float 的偏移量(biasbias)为 127127,所以实际的指数位为 3+127=13010=1000001023 + 127 = 130_{10} = 10000010_2
  • 尾数位(mantissamantissa):隐含掉 1.1. 开头,所以取 001101001101,向右侧补零到 23 位,得到 0011010000000000000000000110100000000000000000

所以完整的 32 位存储就是 0_10000010_00110100000000000000000

将二进制浮点数还原为十进制的数学运算

公式为:

value=(1)sign×(1+mantissa)×2(exponentbias)value = (-1)^{sign} \times (1 + mantissa) \times 2^{(exponent - bias)}

其中:signsign 表示符号位,mantissamantissa 表示尾数位,exponentexponent 表示指数位,biasbias 表示偏移量。

例如:将二进制 0_10000010_00110100000000000000000 还原为十进制。

将其分解为三部分:

  • 符号位(signsign):是 00,带入公式的第一部分 (1)0(-1)^{0}
  • 指数位(exponentexponent):是 1000001010000010,将其转化为十进制 100000102=1301010000010_2 = 130_{10},由于 float 的偏移量(biasbias)为 127127,所以将其带入公式的第三部分 130127=3130 - 127= 3
  • 尾数位(mantissamantissa):是 001101001101,带入公式的第二部分 1+0.0011011 + 0.001101,从而补回隐含的 1.1.

最终计算结果为:

value=10×(1+0.001101)×2130127value = (-1)^0 \times (1 + 0.001101)\times 2^{130 - 127}
value=1.001101×23value= 1.001101 \times 2^3
value=1001.1012 value= 1001.101_2
value=9.62510value = 9.625_{10}

浮点数中的特殊值

由于浮点数的特殊存储格式,所以浮点数中存在一些特殊值,比如:

表示值 指数位 尾数位
±0.0\pm 0.0 全 0 全 0
±Infinity\pm Infinity 全 1 全 0
NaNNaN 全 1 非全 0

例如

// 1.0 除以 0.0 会输出 NaN
double nan = 0.0 / 0.0;
// 1.0 除以 0.0 会输出 Infinity
double infinity = 1.0 / 0.0;

浮点精度问题

在计算机中,浮点数实际上是二进制的科学计数法表示,这种表示方式导致部分数值只能近似存储,因为某些十进制小数无法精确转换为二进制‌。例如,十进制 0.10.1 在二进制中实际上是无限循环小数 0.000110011001100...0.000110011001100...,就像 13\frac{1}{3} 在十进制中表示为 0.333...0.333... 一样‌。

无论是 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;

综上所述,floatdouble 不适合精确计算(如货币),建议用 BigDecimal

加油啊!即便没有转生到异世界,也要拿出真本事!!!\(`Δ’)/
最后更新于 2025-07-10