Java中2L与2的表达式差异及溢出问题解析
两个Java语句的区别与溢出问题解析
先来看你给出的两段代码:
sum=(sum+2L*(A.length-count1[i])*count1[i])%1000000007; sum=(sum+2*(A.length-count1[i])*count1[i])%1000000007;
你的疑问是:
请问上述两个语句有什么区别?这与溢出问题存在怎样的关联?
已知:count1为整数类型数组,sum为long类型。
第一条语句可使代码通过提交,但第二条语句在部分场景下会引发溢出。Java不会自动对代码中的2进行类型转换吗?2L为何会产生不同的结果?
下面我来一步步拆解:
核心差异:字面量的类型决定了运算的基础类型
这两行代码唯一的区别就是2和2L:
2是int类型字面量,而2L是long类型字面量。Java的数值运算遵循「类型提升」规则,但这个提升是按运算顺序逐步触发的,不是全局一次性转换。
第二条语句溢出的原因:中间运算全是int类型
咱们拆解2*(A.length-count1[i])*count1[i]的计算过程:
A.length是int,count1[i]也是int,所以A.length-count1[i]的结果还是int。- 用int类型的
2去乘这个int结果,得到的依然是int值。 - 再和另一个int类型的
count1[i]相乘——如果这几个int的乘积超过了int的最大值(2^31-1,也就是2147483647),就会发生int溢出。溢出后的值会变成错误的数值(比如负数),这时候再把这个错误值加到long类型的sum里,已经无法修正溢出带来的错误了,因为溢出已经发生在int运算的阶段。
为什么2L能避免溢出?
再看2L*(A.length-count1[i])*count1[i]的运算逻辑:
- 第一个运算数是long类型的
2L,根据Java的类型提升规则,当long和int进行运算时,int会被自动提升为long类型。 - 所以
A.length-count1[i]这个int结果会先被转成long,再和2L相乘,得到的是long类型的结果。 - 接下来和
count1[i]相乘时,count1[i]也会被提升为long,整个运算过程都在long类型下进行。long的最大值是2^63-1,远大于int的上限,所以中间步骤不会发生溢出,最终得到的是正确的结果,再加到sum里自然没问题。
关于自动类型转换的误区
你可能会疑惑:sum是long类型,Java为什么不自动把整个表达式转成long?
这是因为Java的运算优先级和类型提升是逐步骤执行的,不是直接把整个表达式的结果转成目标类型。第二条语句里的所有中间运算都是int类型,溢出已经在中间步骤发生了,等结果要赋值给sum的时候,只是把错误的int值转成long,无法挽回溢出的损失。
内容的提问来源于stack exchange,提问作者RAJAT SHARMA
相关产品推荐
相关产品推荐

