当前位置:首页 > 休闲 > java long型溢出 正文

java long型溢出

来源:烟消火灭网   作者:知识   时间:2026-10-17 05:50:22

Java里最经典的型溢溢出场景,大概就是型溢时间戳了。我见过不止一个新手在写代码时,型溢直接用System.currentTimeMillis()乘以某个数,型溢然后塞进int里——结果程序跑着跑着就变成了负数,型溢或者干脆在某个临界点突然归零。型溢这不是型溢算法问题,是型溢类型容量问题。

long是型溢64位有符号整数,最大值是型溢9223372036854775807,约92亿亿。型溢听着挺大对吧?型溢但如果你用它来存“纳秒级时间戳”,那也就够用到2262年。型溢可问题在于,型溢很多人会把long和int混着用,型溢或者在做乘法时忘了强制转换——Java的整数运算默认是int,两个int相乘,结果还是int,哪怕你赋值给long变量,也是在溢出之后才转换的。

举个例子。你写long result = 100000 * 100000 * 100000;,编译器不会报错,但运行时会先按int算,算到第二步就爆了,得到一个负数,然后才赋给long。负数的绝对值比正确结果还大,这数据直接没法用。我见过一个支付系统因为这个bug,某天对账时发现一笔订单金额变成了-294967296,排查了半天,最后发现是某个促销活动里的积分换算公式出了问题。

另一个隐蔽的坑是Math.abs(Long.MIN_VALUE)。这个函数返回的还是Long.MIN_VALUE,因为负数的绝对值在溢出时没有对应的正数。你拿它去排序或者做差值,结果全乱套。有一次我调试一个分布式ID生成器,发现某个节点生成的ID偶尔会跳变,最后定位到是时钟回拨时用了绝对值计算,恰好踩中了这个边界。

避免溢出其实不难,但需要养成几个习惯。第一,所有可能超过21亿的运算,开头就写1L或者L后缀,强制让第一个操作数变成long。第二,用Math.multiplyExact()或者Math.addExact(),这些方法会在溢出时直接抛异常,而不是静默返回错误值。第三,如果做时间戳差值,用Instant.now().toEpochMilli()而不是自己算,减少人为乘除。

说个反直觉的细节:long溢出不是从最大值跳到最小值,而是像钟表一样循环。最大值加1会变成最小值,这种环绕特性在某些算法里反而被利用,比如哈希函数里故意让数值溢出产生均匀分布。但业务代码里遇到这种环绕,基本就是事故。

老实说,JVM的long在64位系统上性能并不差,但很多人因为怕溢出,干脆用BigDecimal处理所有数值,导致计算慢十倍。其实只要在关键节点做好防护,long完全够用。我自己的经验是:凡是涉及“时间戳×速率”或“数量×单价”这类乘法,一律先写成long,再在单元测试里塞几个边界值,比如Long.MAX_VALUE / 2和Long.MAX_VALUE - 1,跑一遍就知道有没有隐患。

最后补一句,别迷信代码审查。溢出bug往往是“看着对,算着错”,审查的人大概率也心算不出86400 * 365 * 100是否超限。最好的防线是工具——IDE的静态检查能提示可能的溢出,但更靠谱的是你自己在写乘法时,多敲一个L,少一点自信。

标签:

责任编辑:时尚