直接强转会无声截断高32位导致时间语义丢失,仅当long值在int范围内(-2147483648~2147483647)才安全,须显式校验范围后再转换。

为什么不能直接用 (int) 强转 long 时间戳
直接写 (int) timestamp 看似简单,但会无声截断高 32 位——如果 timestamp 是毫秒级(比如 1717023456789L),转成 int 后只剩低 32 位(-1128969291),完全失去时间语义,且无任何编译或运行时警告。
这种转换只在时间戳本身确实在 int 范围内才安全,而 Java 的 int 最大值是 2147483647,对应到秒级时间戳仅能表示到 2038-01-19 03:14:07(即“2038 年问题”)。毫秒级则早在 1970-01-25 就溢出了。
判断是否可安全降级:先检查范围再转换
必须显式校验 long 值是否落在 int 可表示区间内,而不是依赖隐式截断。这是安全降级的唯一前提。
- 用
timestamp >= Integer.MIN_VALUE && timestamp 判断 - 注意:不要用
timestamp == (int) timestamp—— 这个比较在溢出时可能因符号扩展产生误判 - 若业务只用秒级时间戳(如 Unix timestamp),且明确不跨 2038 年,可用此方式;毫秒级基本不可行
示例:
long tsSec = 1717023456L; // 2024 年某秒级时间戳 if (tsSec >= Integer.MIN_VALUE && tsSec <h3>替代方案:用 <code>Math.toIntExact()</code> 捕获溢出</h3> <p><code>Math.toIntExact(long)</code> 是 JDK 8+ 提供的安全转换工具,它在溢出时抛出 <code>ArithmeticException</code>,比静默截断更可控。</p>
- 适合需要明确失败反馈的场景(如配置加载、数据校验)
- 避免在性能敏感循环中频繁调用——异常开销大
- 它本质就是做了和上一节相同的范围检查,只是封装成了标准 API
示例:
try {
int safeInt = Math.toIntExact(timestamp);
} catch (ArithmeticException e) {
throw new IllegalArgumentException("timestamp out of int range: " + timestamp, e);
}
真正省空间的思路不是硬转 int,而是换结构
单纯把 long 字段改成 int 并不能保证节省内存——JVM 对象字段有对齐填充规则,单个字段节省未必反映到对象总大小上;而且一旦时间范围受限,后续扩展成本极高。
更务实的做法:
- 用
short或byte存「相对于某个基点的偏移量」,比如记录距当天 0 点的秒数(int都嫌大) - 序列化时用
Varint编码(如 Protobuf 的sint32)压缩小数值 - 数据库里用
DATE/TIMESTAMP类型而非BIGINT,由存储层优化
硬塞 int 最容易被忽略的点:你省下的那 4 字节,可能换来一个未来不得不重跑全量数据迁移的凌晨。










