【Java数据类型】Java底层数据类型原理(上):基本类型、转换与封装核心

作者介绍:大家好,我是 CodeStats。一个在底层技术上"考古"了四年的硬核爱好者,也是 WWAIC(全周项目AI编程)范式的提出者和实践者。我曾手写过一个完整的 Java Web 框架(从 IoC 容器到嵌入式 Tomcat,代码全开源),也喜欢用通俗的语言拆解 CPU、JVM、操作系统的运行本质。我的技术信条:所有高深的技术,最后都能用大白话讲清楚。如果讲不清楚,说明还没真正理解。


本文能获得什么

  • 掌握 Java 八种基本数据类型的底层存储原理 :理解补码、IEEE 754、JVM 存储模型,搞懂为什么 0.1 + 0.2 != 0.3

  • 搞懂数字、字符、字节数组的互相转换本质 :从 parseInt 的逐字符累加到 getBytes 的编码查表,彻底理解转换底层

  • 理解封装类、BigDecimal 的设计原理 :知道封装类缓存机制、BigDecimal 的 unscaledValue × 10^-scale 设计


目录

  • 提问一:Java 类都是基于底层基础数据类型封装,Java 八种基本数据类型存储原理

  • 提问二:Java 最核心三类数据类型------数字、字符、字节数组是如何互相转换的,代码示例演示原理

  • 提问三:Java 的封装类型底层原理和日期、BigDecimal 的实现原理


提问一:Java 类都是基于底层基础数据类型封装,Java 八种基本数据类型存储原理

1.1 计算机底层的原子:比特

计算机里最终只有比特------0 和 1。所有数据类型,本质上都是对固定长度比特序列的约定解释。

text

复制代码
8 个比特  → 一个字节
32 个比特 → 一个 int
64 个比特 → 一个 long 或 double

Java 的八种基本类型,就是对固定长度比特序列的不同解释方式。

1.2 整数类型:补码

Java 的 byte、short、int、long 全部使用二进制补码存储。补码的设计解决了正负运算统一和 +0/-0 两个零的问题。

补码规则:

  • 正数的补码就是其本身

  • 负数的补码 = 反码 + 1

  • 最高位是符号位,0 表示正数,1 表示负数

类型 位数 范围
byte 8 -128 ~ 127
short 16 -32768 ~ 32767
int 32 -2³¹ ~ 2³¹-1
long 64 -2⁶³ ~ 2⁶³-1

示例:int 类型的 -1

text

复制代码
原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111  (即 0xFFFFFFFF)

所以 int 类型的 0xFFFFFFFF 就是 -1。

溢出本质 :补码的位数固定,超出范围就截断。Integer.MAX_VALUE + 1 = Integer.MIN_VALUE,因为 32 位补码的 0x7FFFFFFF + 1 = 0x80000000,最高位变成 1,解释为负数。

1.3 浮点类型:IEEE 754

float 和 double 使用 IEEE 754 标准,把 32/64 位拆成三部分:

text

复制代码
float  = 1 位符号 + 8 位指数 + 23 位尾数
double = 1 位符号 + 11 位指数 + 52 位尾数

值的计算公式:

text

复制代码
(-1)^符号 × 1.尾数 × 2^(指数 - 偏移)

为什么 0.1 + 0.2 != 0.3?

因为十进制小数 0.1 在二进制中是无限循环小数,无法用有限位精确表示。double 只能存储它的近似值。两个近似值相加,结果也是近似值,所以显示为 0.30000000000000004。

1.4 char:16 位无符号整数

char 是 16 位无符号整数,表示一个 UTF-16 代码单元。'A' 就是 65,'中' 就是 0x4E2D(20013)。

java

复制代码
char c = '中';
System.out.println((int) c); // 20013

1.5 boolean:JVM 内部用 int 或 byte

JLS 没有规定 boolean 的大小。JVM 规范中,boolean 在局部变量和操作数栈里通常按 int 处理,在数组中可能按 byte 处理。

1.6 基本类型的 JVM 存储位置

  • 局部变量:存放在 JVM 虚拟机栈的局部变量表中

  • 成员变量(未被 static 修饰) :存放在 Java 堆中

  • 静态变量:存放在方法区(JDK 8 后在元空间)


提问二:Java 最核心三类数据类型------数字、字符、字节数组是如何互相转换的

2.1 字符与数字:本质是编码表映射

字符 '0' ~ '9' 的 ASCII 码是 48 ~ 57。所以:

java

复制代码
int digit = '9' - '0';         // 9
char c = (char) ('0' + 9);    // '9'

2.2 字符串 → 整数:逐字符乘 10 加位

Integer.parseInt("123") 的底层逻辑:

java

复制代码
int result = 0;
for (int i = 0; i < s.length(); i++) {
    int digit = s.charAt(i) - '0';
    result = result * 10 + digit;
}

同时做溢出检查------一旦发现累加会溢出,立即抛出 NumberFormatException,不需要读完整个字符串。

2.3 整数 → 字符串:除 10 取余

Integer.toString(123) 的底层逻辑:

java

复制代码
// 简化版
char[] buf = new char[10];
int i = buf.length;
while (n > 0) {
    buf[--i] = (char) ('0' + n % 10);
    n /= 10;
}

JDK 源码中使用了 DecimalDigits 工具类,通过查表加速转换。

2.4 字符串 → 字节数组:按 Charset 编码

这不是强转,而是编码查表:

java

复制代码
byte[] utf8 = "中".getBytes(StandardCharsets.UTF_8);
// [-28, -72, -83],即 E4 B8 AD

byte[] gbk = "中".getBytes("GBK");
// [-42, -48],即 D6 D0

同一个"中",UTF-8 是 3 字节,GBK 是 2 字节。

2.5 字节数组 → 字符串:按 Charset 解码

java

复制代码
String s = new String(new byte[]{(byte)0xE4, (byte)0xB8, (byte)0xAD},
                      StandardCharsets.UTF_8);
// "中"

2.6 数字 ↔ 字节数组:移位 + 掩码

int → byte4:

java

复制代码
int x = 0x12345678;
byte[] bs = new byte[4];
bs[0] = (byte)(x >>> 24);            // 0x12
bs[1] = (byte)((x >>> 16) & 0xFF);   // 0x34
bs[2] = (byte)((x >>> 8) & 0xFF);    // 0x56
bs[3] = (byte)(x & 0xFF);            // 0x78

byte4 → int:

java

复制代码
int y = ((bs[0] & 0xFF) << 24)
      | ((bs[1] & 0xFF) << 16)
      | ((bs[2] & 0xFF) << 8)
      |  (bs[3] & 0xFF);

& 0xFF 的作用:byte 是有符号的,0xE4 作为 byte 是 -28,转成 int 时会符号扩展成 0xFFFFFFE4。& 0xFF 只保留低 8 位,变成干净的 228。

2.7 转换规律总表

转换 底层做法
char → int 零扩展,char 无符号
int → char 取低 16 位
'9' → 9 c - '0'
9 → '9' (char)('0' + 9)
String → int result = result * 10 + digit,溢出检查
int → String % 10、/ 10,反转
String → byte[] Charset 编码查表
byte[] → String Charset 解码查表
int → byte[] >>> 移位 + & 0xFF 掩码
byte[] → int & 0xFF + << + `

提问三:Java 的封装类型底层原理和日期、BigDecimal 的实现原理

3.1 封装类:内部就是一个基本类型字段

封装类 内部字段
Byte final byte value
Short final short value
Integer final int value
Long final long value
Float final float value
Double final double value
Character final char value
Boolean final boolean value

Integer 类通过 valueOf() 方法实现自动装箱,自动拆箱则通过继承自 Number 类的 intValue() 方法实现。

3.2 缓存机制

Integer、Long、Short、Byte 默认缓存 -128 到 127 之间的值,Character 缓存 0 到 127,Boolean 直接返回 TRUE/FALSE。

java

复制代码
Integer a = 100, b = 100;
System.out.println(a == b); // true,同一缓存对象

Integer c = 1000, d = 1000;
System.out.println(c == d); // false,不同对象

3.3 BigDecimal:BigInteger + scale

BigDecimal 的核心设计:

text

复制代码
BigDecimal = unscaledValue × 10^(-scale)

内部由 BigInteger intVal 和 int scale 组成。

值 unscaledValue scale
123 123 0
-1.5 -15 1
1e10 1 -10

运算规律:

  • 加法:对齐 scale,再 BigInteger 加

  • 乘法:unscaledValue 相乘,scale 相加

  • 除法:可能除不尽,必须指定舍入模式

  • compareTo 只比数值,equals 还比 scale

3.4 日期类的底层:long 时间戳

java.util.Date 内部只有一个 long fastTime,表示自 1970-01-01T00:00:00 UTC 以来的毫秒数。java.sql.Timestamp 额外有一个 int nanos 表示纳秒部分。


总结

上篇从最底层的比特出发,梳理了 Java 八种基本类型的存储原理(补码、IEEE 754、UTF-16),然后深入数字、字符、字节数组的互相转换本质(乘 10 加位、除 10 取余、移位掩码、编码查表),最后剖析了封装类的字段结构、缓存机制以及 BigDecimal 的 unscaledValue × 10^-scale 设计。这些是 Java 数据类型体系的基石。


👍 点赞 · ⭐ 收藏 · 🔔 关注

如果本文对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续输出底层干货的最大动力!

相关推荐
凤凰院凶涛QAQ27 分钟前
《Java版数据结构 & 集合类剖析》Map & Set & 反射:“从哈希碰撞到红黑树转换,反射帮你扒开 HashMap 的桶数组看个究竟”
java·数据结构·算法
H.莓飛30 分钟前
【数据结构】排序_OJ题(一):计数、桶与排序后的处理
java·开发语言·数据结构
wangchunyu1141 小时前
JAVA AQS与Lock实现原理(含CAS)
java·开发语言·word
w_zero_one2 小时前
算法--模拟
java·数据结构·算法
Javatutouhouduan2 小时前
阿里1658页Java面试题总结(含答案)
java·高并发·java面试·java面试题·后端开发·java程序员·java八股文
秋天的怀念_David3 小时前
算法一千零一夜 · 第十三夜 幻方,武侠世界里的数学迷宫
java·程序人生·算法
专业程序开发源3 小时前
springboot欧缇蔓月子中心管理系统82697-计算机课程设计、毕业设计
java·spring boot·后端·python·spring·django·课程设计
秋天的怀念_David3 小时前
算法一千零一夜 · 第十四夜 幻方与奇妙的二进制
java·程序人生·算法
thefool1122663 小时前
网络编程^
java
Thinker QAQ4 小时前
并发编程(十一):Shared Memory 与 Message Passing——关系与组合使用
java·python·go·并发编程·shared memory·message passing