解决“三元运算符”中i++和++i不一致问题
前言
我们都知道i++和++i两个的赋值方式不一样,i++是先赋值在累加,++i是先累加在赋值。
问题发现
在业务代码编写的过程中,为了使代码简洁,使用三元运算符进行选择后累加,代码如下所示:
public class Test {
public static void main(String[] args) {
Integer i = 0;
i = i == null ? 1 : i++;
System.out.println(i);
}
}
执行后,你会发现结果为0,并没有进行所谓的累加,为此进行深入探讨。
问题解决
下面通过a++和++b对比来进行讲解
public class Test {
public static void main(String[] args) {
Integer a = 0;
a++;
System.out.println(a);
Integer b = 0;
++b;
System.out.println(b);
/** Output:
* 1
* 1
*/
}
}
编译后:
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
var1 = var1 + 1;
System.out.println(var1);
Integer var2 = 0;
var2 = var2 + 1;
System.out.println(var2);
}
}
我们先通过简单的a++和++b,通过编译后的字节码文件,可以看到,两个都进行了加1赋值,可以看到最后的结果输出都是1。
我们换种方式如果直接打印a++和++b,结果会如何:
public class Test {
public static void main(String[] args) {
Integer a = 0;
System.out.println(a++);
Integer b = 0;
System.out.println(++b);
/** Output:
* 0
* 1
*/
}
}
编译后
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
PrintStream var10000 = System.out;
var1 + 1;
var10000.println(var1);
Integer var2 = 0;
System.out.println(var2 + 1);
}
}
很显然a++在字节码文件中,加1后并未赋值,这也就得知a++的结果为0;而++b你会发现,始终保持加1状态,所以结果为1。
回归正题,在“三元运算符”中,a++和++b执行原理又会如何呢?
public class Test {
public static void main(String[] args) {
Integer a = 0;
a = a == null ? 1 : a++;
System.out.println(a);
Integer b = 0;
b = b == null ? 1 : ++b;
System.out.println(b);
/** Output:
* 0
* 1
*/
}
}
编译后:
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
int var10000;
if (var1 == null) {
var10000 = 1;
} else {
var1 + 1;
var10000 = var1;
}
var1 = var10000;
System.out.println(var1);
Integer var2 = 0;
var2 = var2 == null ? 1 : Integer.valueOf(var2 + 1);
System.out.println(var2);
}
}
我们通过字节码文件可以看到,a++的“三元运算符”被拆分成了if else
语句,将值赋给新的变量,a++在执行加1后并未赋值,这也就导致最终赋值给变量的值为0;++b和预期一样加1,只不过“三元运算符”中不能直接进行累加操作,所以通过valueOf()
给他进行了包裹。
我们再来看下正常的if else
语句中,a++是一个怎样的执行过程:
public class Test {
public static void main(String[] args) {
Integer a = 0;
if(a == null){
a =1;
}else{
a++;
}
System.out.println(a);
/** Output:
* 1
*/
}
}
编译后
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
if (var1 == null) {
var1 = 1;
} else {
var1 = var1 + 1;
}
System.out.println(var1);
}
}
可以看到直接进行加1操作,最终结果为1。
最后我们再来讲解,循环体中的a++是如果执行的:
public class Test {
public static void main(String[] args) {
Integer a = 0;
for (int i = 0; i < 5; i++) {
a = a++;
System.out.print(a);
}
System.out.println();
for (int i = 0; i < 5; i++) {
System.out.print(a++);
}
/** Output:
* 00000
* 01234
*/
}
}
编译后
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
int var2;
Integer var3;
for(var2 = 0; var2 < 5; ++var2) {
var3 = var1;
var1 + 1;
var1 = var1;
System.out.print(var3);
}
System.out.println();
for(var2 = 0; var2 < 5; ++var2) {
PrintStream var10000 = System.out;
var3 = var1;
var1 = var1 + 1;
var10000.print(var3);
}
}
}
第一个循环体,定义一个新变量赋值后,进行加1并没赋值,所以每次遍历输出的结果都一样,当然没有人会在累加1操作后再重新赋值,这会是一个bug,要注意。第二个循环体就很清楚了,先赋值后累加,所以输出的结果为:01234。
总结
通过前面讲解的例子,我们发现i++是先赋值在累加,++i是先累加在赋值,这种说法不一定完全正确,或者说i++这种说法得分情况来说。++i我们就不进行过多的总结了,得到的结果总会先加1。下面我们把之前i++的例子归纳一下:
为什么i++累加后未赋值?
前面讲解的例子中发现字节码文件中加1未赋值的情况,这种情况应该分两种,一种是正常情况下的累加,另一种是“三元运算符”的累加。
- 先来讲解正常情况下的累加
public class Test {
public static void main(String[] args) {
Integer a = 0;
System.out.println(a++);
Integer b = 0;
System.out.println(b++);
b++;
System.out.println(b);
/** Output:
* 0
* 0
* 2
*/
}
}
编译后
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
PrintStream var10000 = System.out;
var1 + 1;
var10000.println(var1);
Integer var2 = 0;
var10000 = System.out;
Integer var3 = var2;
var2 = var2 + 1;
var10000.println(var3);
var2 = var2 + 1;
System.out.println(var2);
}
}
编译后,我们看到第一种情况没有在进行赋值,所以结果为0;第二种情况可以看到累加的时候赋值了,而且是先赋值后累加的。总结:累加时是否需要赋值,是取决于接下来是否还需要对这个值进行操作,并不代表没有进行累加。
- 再来讲解“三元运算符”的累加
public class Test {
public static void main(String[] args) {
Integer a = 0;
a = a == null ? 1 : a++;
System.out.println(a);
a++;
System.out.println(a);
/** Output:
* 0
* 1
*/
}
}
编译后
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
int var10000;
if (var1 == null) {
var10000 = 1;
} else {
var1 + 1;
var10000 = var1;
}
var1 = var10000;
System.out.println(var1);
var1 = var1 + 1;
System.out.println(var1);
}
}
与正常累加有所不同,后面累加对这个值的操作,丝毫不影响“三元运算符”操作。通过字节码文件,我们发现赋值时,将值赋给var10000变量,但是此过程中累加时并未赋值操作,然后再去通过var10000赋值给var1 变量得到最终结果,所以导致此结果为0,再来看一段代码(如下)
public class Test {
public static void main(String[] args) {
Integer a = 0;
if (a == null) {
a = 1;
} else {
System.out.println(a++);
}
/** Output:
* 0
*/
}
}
编译后
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
if (var1 == null) {
var1 = 1;
} else {
PrintStream var10000 = System.out;
var1 + 1;
var10000.println(var1);
}
}
}
通过上面这段代码我想答案已经出来了,为什么没有赋值?因为再“三元运算符”中或者上面这段代码中,a++之后并没有再做任何操作,“三元运算符”代码后的累加操作,不属于此代码块的生命周期内。所以并未赋值,结果为0。再没有了解原理之前,你可能会说,因为a++是先累加后赋值,这样说法没错,但只说对一半。
为什么i++不一定是先赋值再累加?
看过前面的案例你就清楚了,很简单,这取决于你累加后是否还会有操作,以循环体为例,再来进行讲解:
public class Test {
public static void main(String[] args) {
Integer a = 0;
for (int i = 0; i < 5; i++) {
a++;
System.out.print(a);
}
System.out.println();
Integer b = 0;
for (int i = 0; i < 5; i++) {
System.out.print(b++);
}
/** Output:
* 12345
* 01234
*/
}
}
编译后
public class Test {
public Test() {
}
public static void main(String[] var0) {
Integer var1 = 0;
for(int var2 = 0; var2 < 5; ++var2) {
var1 = var1 + 1;
System.out.print(var1);
}
System.out.println();
Integer var6 = 0;
for(int var3 = 0; var3 < 5; ++var3) {
PrintStream var10000 = System.out;
Integer var4 = var6;
var6 = var6 + 1;
var10000.print(var4);
}
}
}
通过字节码代码,你会发现如果累加后还有操作,哪结果就会先累加再赋值,所以第一个循环体结果为12345;如果后面没有操作了就是先赋值再累加,第二个循环体结果为01234。
经过这么些个案例讲解,希望能解决你疑惑,给你带来收获,欢迎交流。