Java自增运算符查询(++i和i++)

12

我有以下代码:

public class Book {
    private static int sample1(int i) {
        return i++;
    }
    private static int sample2(int j) {
        return ++j;
    }

    public static void main(String[] arguments){ 
        int i = 0;
        int j = 0;
        System.out.println(sample1(i++)); //0
        System.out.println(sample1(++i)); //1
        System.out.println(sample2(j++));//1
        System.out.println(sample2(++j));//2
        System.out.println(i);//2
        System.out.println(j);//2
    }
}

我的期望输出在注释中。实际输出如下:

0
2
1
3
2
2

我对函数调用和自增运算符感到困惑。有人能够友好地解释一下实际结果吗?


2
你一遍又一遍地增加同一个变量。每次想要增加它时,你需要将其重置为零。 - ctzdev
2
你知道Java是按值传递的,所以sample1实际上只是return i;,而sample2是return j + 1,对吧?哎呀!错别字已经修正。谢谢@ElliottFrisch。 - David Ehrmann
1
@DavidEhrmann 你的意思是按值传递吗? - awksp
1
一个是前置,一个是后置。这有帮助吗? - Elliott Frisch
1
@ChrisTarazi 我知道我一遍又一遍地增加同一个变量。我只是想要解释一下这些值是如何逐步增加的。 - misguided
显示剩余2条评论
7个回答

16

由于sample1sample2仅修改它们自己的局部变量ij(而不是调用方法的那些变量),如果我们在没有这些修改的情况下重写它们,将更加清晰:

private static int sample1(int i) {
    return i;   // was 'i++', which evaluates to the old i
}
private static int sample2(int j) {
    return j + 1;   // was '++j', which evaluates to j after incrementing
}

这时,只需直接将它们替换掉即可 — sample1(...) 变为 ...,而 sample2(...) 则变为 ... + 1

int i = 0;
int j = 0;
System.out.println(i++);
System.out.println(++i);
System.out.println((j++) + 1);
System.out.println((++j) + 1);
System.out.println(i);
System.out.println(j);

我们可以通过将增量单独作为命令来使其更清晰。 i++ 求值为 i 的原始值,因此它类似于在运行周围命令之后增加 i; 相比之下,++i 是在运行周围命令之前增加 i。所以我们得到:

int i = 0;
int j = 0;
System.out.println(i);
i++;
++i;
System.out.println(i);
System.out.println(j + 1);
j++;
++j;
System.out.println(j + 1);
System.out.println(i);
System.out.println(j);

在这一点上,跟踪并查看它将输出什么应该很简单。

这样说清楚了吗?


1
这是否意味着在方法 sample1 中,return ireturn i++ 是相同的东西? - misguided
1
是的,因为Java通过值传递方法参数,而不是引用传递。在"sample1"中的局部变量"i"在增加之前就超出了其作用域。 - Jason Baker
OP 刚刚修改了:System.out.println(j); System.out.println(j); 改为 System.out.println(i); 你能否也更新一下你的答案?他打印了同一个变量两次(虽然不影响你的答案,但还是请更新一下)。 - Nenad Bulatović
@NenadBulatovic:完成了!感谢你的提醒。 - ruakh

13

首先你需要知道 x++++X 之间的区别;

对于 x++ 的情况:

首先使用当前值,然后将其递增。这意味着您将获得操作的当前值 x, 如果您下次使用 x,则会获得递增的值;

对于 ++x 的情况:

首先将当前值递增,然后使用它(即递增后的值)。这意味着您将在此操作中获得递增的值, 并在此操作之后及其他操作中继续使用该值。

现在让我们拆分代码并分别讨论它们

方法:sample1():

private static int sample1(int i) {
    return i++;
}

这个方法会接收一个整数并返回它,然后尝试对其进行递增操作,但由于在返回变量i后,该变量已经超出了作用域,所以它根本不会被递增。 例如:输入10-> ���出10

方法:sample2()

private static int sample2(int j) {
    return ++j;
}

该方法将接收一个整数并将其先加一然后返回。 例如: 10-> 输出 11

在两种情况下,只有变量在本地更改,这意味着如果您从主方法调用,则主方法的变量不会受到更改的影响 (因为sample1()和sample2()正在复制变量)

现在是主方法的代码

System.out.println(sample1(i++)); // it's giving sample1() `i=0` then making `i=1` 
                                  //  so  sample1() will return 0 too;

System.out.println(sample1(++i)); // it's making `i=2` and then giving sample1() `i=2`
                                  // so sample1() will return 2;

System.out.println(sample2(j++)); // it's giving sample2() `j=0` then making `j=1` 
                                  // so sample2() will return 1;

System.out.println(sample2(++j)); // it's making `j=2` giving sample2() `j=2` then  
                                  // so sample2() will return 3;

8
你正在体验前缀和后缀运算符的乐趣。
前缀运算符++i在表达式中使用变量i之前将其加1,而后缀运算符(i++)在表达式中使用i之后再将其加1。
这意味着你的方法sample1什么也没做; 它计算包含i的表达式,但由于该表达式是返回语句,因此局部变量i超出范围,我们无法再修改它了。
相比之下,sample2在返回之前增加了局部变量j的副本,这就是为什么您打印的j值比预期高的原因。

2

它们都通过 i = i + 1; 来增加变量 i。

不同之处在于

++i 先增加值,然后返回它

i++ 先返回值,然后增加它

这种行为差异在 for 循环中并不重要。

如果你想了解差异,请尝试以下操作:

int x = 0;
int y = x++;

int x = 0;
int y = ++x;

这里x++会先返回值再将其增加,而++x会先将值增加再返回


2

举个例子,

  private static int sample1(int i) {
    return i++;
  }
  private static int sample2(int j) {
  return ++j;
  }

  public static void main(String[] arguments)
  { 
  int i = 0;
  int j = 0;
  System.out.println(sample1(i++)); //0
  System.out.println(sample1(++i)); //1
  System.out.println(sample2(j++));//1
  System.out.println(sample2(++j));//2
  System.out.println(j);//2
  System.out.println(j);//2
  }
  1. i = 0; sample1(i++) -> 这里先将0传入sample1函数,然后返回i++。因此,println输出的是0,但是最终i的值为1。
  2. i = 1; sample1(++i) -> 这里先将2传入sample1函数,然后返回i++。因此,println输出的是2。
  3. j = 0; sample2(j++) -> 这里先将0传入sample2函数,然后返回++j。因此,println输出的是1。
  4. j = 1; sample2(++j) -> 这里先将2传入sample2函数,然后返回++j。因此,println输出的是3。但是增量操作在sample2函数内完成,而不是在主函数中,因此j仍然保持为2。
  5. j = 2
  6. j = 2

2

简单易懂:
1)第一次调用:
a)提供i(==0)给sample1(),它返回0(然后增加参数i,但被丢弃)。
b)由于i++,增加i。 i现在是1
c)打印函数结果:0。

2)第二次调用:
a)由于++i,增加i。 i现在是2
b)提供i(==2)给sample1(),它返回2(然后增加参数i,但被丢弃)。
c)打印函数结果:2。

3)第三次调用:
a)提供j(==0)给sample2(),它增加参数并因此返回1。
b)由于j++,增加j。 j现在是1
c)打印函数结果:1。

4)第四次调用:
a)由于++j,增加j。 j现在是2
b)提供j(==2)给sample2(),它增加参数并因此返回3。
c)打印函数结果:3。

5和6)第五和第六次调用:
a)打印j的值:2。

记住这里的关键是i++在将其作为参数传递后增加变量,而++i在将其作为参数传递前增加变量。

希望这有所帮助


为什么它会被丢弃? - misguided
2
嗨@misguided,它被丢弃是因为方法参数的作用域仅限于方法本身:这意味着当方法返回其结果时,您得到的是变量的副本,在sample1()中,例如,增量i++操作发生在您获得方法结果之后。Java始终按值传递方法参数,而不是按引用传递(与C++或VB相反,您有此选项)... - NotSoOldNick
因此,您永远不能将原始类型变量提供给函数,在该函数中修改它,并希望在该函数之外看到修改:您将看到修改的唯一方法是首先通过本地参数向方法提供副本,然后通过返回修改后的参数的副本回到代码的调用部分。 - NotSoOldNick
这基本上是@Jason在他对ruakh答案的评论中所解释的。 - NotSoOldNick

2

第一次打印

调用之前:i = 0

调用后自增

以0的值调用sample1

sample1返回0,自增被丢弃

调用后:i = 1

第二次打印

调用之前:i = 1

调用之前自增

以2的值调用sample1

sample1返回2,自增被丢弃

调用后:i = 2

第三次打印

调用之前:j = 0

调用后自增

以0的值调用sample2

sample2将0递增到1,并返回它

打印1

将j递增到1

调用后:j = 1

第四次打印

调用之前:j = 1

调用之前自增

将j递增到2

以2的值调用sample2

sample2将2递增到3,并返回它

打印3

调用后:j = 2

第五次打印

打印i

打印2

第六次打印

打印j

打印2


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接