Java i++和++i问题详解

运算符优先级

Java运算符优先级

i++和++i的区别

  1. i++, 先用i的值参与计算,然后再自增1。如:
    i=4;
    a=i++; //此时,先将i的值赋给a,i再自增,故a=4,i=5
  2. ++i,先自增1,再参与计算。如:
    i=4;
    a=++i; //此时,i先自增1,再将值赋给a,故a=5,i=5

例题

public class AutoIncreaseTest{
    public static void main(String[] args){
        int i=0;
        for( ; i<10; i++){
            System.out.print(i + " ");           //输出: 0 1 2 3 4 5 6 7 8 9 

        }
        System.out.println();
        System.out.println("After For: " + i);   //输出: After For: 10
       
        int j=0;
        for( ; j<10; ++j){
            System.out.print(j + " ");           //输出: 0 1 2 3 4 5 6 7 8 9 

        }
        System.out.println();
        System.out.println("After For: " + j);   //输出: After For: 10

        int a = 0;
        a = a ++;
        System.out.println("a = " + a);         //输出: a = 0

        int b = 0;
        b = ++ b;                               //IDE提示: The assignment to variable b has no effect
        System.out.println("b = " + b);         //输出: b = 1

        int c = 0;
        int d = 0;
        int e = 0;
        for (int k = 0; k < 99; k++) {
            c = c ++;
            e = d ++;
        }
        System.out.println("c = " + c);         //输出: c = 0
        System.out.println("d = " + d);         //输出: d = 99
        System.out.println("e = " + e);         //输出: e = 98

        int f = 0;
        int g = 0;
        int h = 0;
        for (int k = 0; k < 99; k++) {
            f = ++ f;                           //IDE提示: The assignment to variable f has no effect
            h = ++ g;
        }
        System.out.println("f = " + f);         //输出: f = 99
        System.out.println("g = " + g);         //输出: g = 99
        System.out.println("h = " + h);         //输出: h = 99
    }
}

i++ 和 ++i的实现原理

public class Test {

    public void testIPlus() {
        int i = 0;
        int j = i++;
    }

    public void testPlusI() {
        int i = 0;
        int j = ++i;
    }

}

将上面的源代码编译之后,使用“ javap -c Test ”命令查看编译生成的代码(忽略次要代码)如下:

...
{
  ... 

  public void testIPlus();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=3, args_size=1
         0: iconst_0               // 生成整数0
         1: istore_1               // 将整数0赋值给1号存储单元(即变量i)
         2: iload_1                // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0)
         3: iinc          1, 1     // 1号存储单元的值+1(此时 i=1)
         6: istore_2               // 将数据栈顶的值(0)取出来赋值给2号存储单元(即变量j,此时i=1,j=0)
         7: return                 // 返回时:i=1,j=0
      LineNumberTable:
        line 4: 0
        line 5: 2
        line 6: 7

  public void testPlusI();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=3, args_size=1
         0: iconst_0                // 生成整数0
         1: istore_1                // 将整数0赋值给1号存储单元(即变量i)
         2: iinc          1, 1      // 1号存储单元的值+1(此时 i=1)
         5: iload_1                 // 将1号存储单元的值加载到数据栈(此时 i=1,栈顶值为1)
         6: istore_2                // 将数据栈顶的值(1)取出来赋值给2号存储单元(即变量j,此时i=1,j=1)
         7: return                  // 返回时:i=1,j=1
      LineNumberTable:
        line 9: 0
        line 10: 2
        line 11: 7
}
...

i=i++ 和 i=++i的实现原理

i=i++

int i = 0;
i = i++;

System.out.println("i=" + i); // 输出 i=0 

使用javap查看编译生成的代码如下:

0: iconst_0                          // 生成整数0
1: istore_1                          // 将整数0赋值给1号存储单元(即变量i,i=0)
2: iload_1                           // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0)
3: iinc          1, 1                // 1号存储单元的值+1(此时 i=1)
6: istore_1                          // 将数据栈顶的值(0)取出来赋值给1号存储单元(即变量i,此时i=0)
7: getstatic     #16                 // 下面是打印到控制台指令
10: new           #22               
13: dup
14: ldc           #24                 
16: invokespecial #26                 
19: iload_1
20: invokevirtual #29                
23: invokevirtual #33                 
26: invokevirtual #37                 
29: return

从编码指令可以看出,i 被栈顶值所覆盖,导致最终 i 的值仍然是 i 的初始值。无论重复多少次 i = i++ 操作,最终 i 的值都是其初始值。可将其理解为:

  1. i++有三项操作:
    将值赋给中间变量int temp=i;
    i=i+1;
    return i;
  2. i=i++有四项操作:
    将值赋给中间变量int temp=i;
    i=i+1;
    i=temp;
    return i;

i = ++i

int i = 0;
i = ++i;                       // IDE提示“The assignment to variable i has no effect”警告

System.out.println("i=" + i);  // 输出i=1

使用 i = ++i 得到了预期的结果,但同时IDE提示“The assignment to variable i has no effect”警告,警告的意思是将值赋给变量 i 毫无作用,并不会改变i的值。也就是说:i = ++i 等价于 ++i

++i 和 i++不是原子操作

虽然在Java中 ++i 是一条语句,字节码层面上也是对应 iinc 这条JVM指令,但是从最底层的CPU层面上来说,++i 操作大致可以分解为以下3个指令:

  1. 取数
  2. 累加
  3. 存储

其中的一条指令可以保证是原子操作,但是3条指令合在一起却不是,这就导致了 ++i 语句不是原子操作。

如果要保证累加操作的原子性,可以采取下面的方法(使用volatile修饰变量i也无法保证++i是原子操作):

  1. ++i 置于同步块中,可以是synchronized或者J.U.C中的排他锁(如ReentrantLock等)。
  2. 使用原子性(Atomic)类替换 ++i ,具体使用哪个类由变量类型决定。如果 i 是整形,则使用AtomicInteger类,其中的AtomicInteger#addAndGet()就对应着 ++i 语句,不过它是原子性操作。
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值