【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 的 byteshortintlong 全部使用二进制补码存储。补码的设计解决了正负运算统一和 +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

floatdouble 使用 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 缓存机制

IntegerLongShortByte 默认缓存 -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 intValint 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 数据类型体系的基石。


👍 点赞 · ⭐ 收藏 · 🔔 关注

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

相关推荐
陈皮波比茶1 小时前
SpringCloudAlibaba
java·spring cloud·微服务
重生之小比特1 小时前
【Java SE】程序逻辑控制
java·开发语言·python
find1star1 小时前
LeetCode 25:K 个一组翻转链表
java·数据结构·算法·leetcode·链表·职场和发展·动态规划
染的人1 小时前
Java 10x15cm面单PDF转换A4格式PDF
java·开发语言·pdf
泡海椒1 小时前
JQuick-java (JQuick-ASM)性能优化原理:字节码生成与缓存机制深度解析
java·缓存·性能优化
Bs_MoneyMagnet1 小时前
基于springboot+vue的家居生活商城平台的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring
微三云 - 廖会灵 (私域系统开发)1 小时前
Spring Boot实战:五级分销定价与自动分佣系统设计与实现
java·spring boot·后端
Json____1 小时前
从零构建在线拍卖系统:Java 全栈开发实践
java·开发语言·管理系统·it学习·wwwoop.com
我命由我123451 小时前
Android Compose 开发,使用 ConstraintLayout,但是引入的是旧的 ConstraintLayout
android·java·java-ee·android studio·android jetpack·android-studio·android runtime