【问题标题】:why the output shown like that ? java为什么这样显示的输出?爪哇
【发布时间】:2016-10-30 20:26:27
【问题描述】:

我有点困惑,为什么输出给了我 m = 1111 的值??? 这是代码:

class X
{
    int m = 1111;

    {
        m = m++;

        System.out.println(m);
    }
}

class Y extends X
{
    {
        System.out.println(methodOfY());
    }

    int methodOfY()
    {
        return m-- + --m;
    }
}

public class MainClass
{
    public static void main(String[] args)
    {
        Y y = new Y();
    }
} 

你能解释一下为什么输出是这样的吗?

【问题讨论】:

  • 注意:m = m++ 不做任何事情。
  • 因为执行顺序:首先X的变量被初始化(m = 1111),然后X'初始化块被执行(m = m++; System.out.println(1111);m = m++;不改变@ 987654328@ 的值),然后执行 Y 的初始化块(System.out.println(m-- + --m);,这将是 2200)。
  • 你期望的输出是什么?
  • @Turing85 最后一个数字不应该是2220吗?
  • 因为在创建类的实例时会运行类中未命名的实例初始化程序块。与往常一样,首先构造基类,然后构造派生类。所以m被初始化为1111,然后m被赋值为m++的后增量值,但首先发生赋值,然后增量。所以这是一个无操作。然后打印m,得到1111。之后调用methodOfY,更改m 并打印它。这可能不会与构造函数而不是未命名的初始化程序混淆,因为人们更习惯于构造函数。

标签: java output increment


【解决方案1】:

执行顺序是从超类到子类定义的,在类内,首先执行变量的静态初始化,然后执行初始化块。这给出了以下顺序:

class X {
    int m = 1111; // 1st point visited

    // This is X's initializer block:
    {
        m = m++; // 2nd point visited: m's value is not changed, read below
        System.out.println(m);
    }
}

class Y extends X {
    // This is Y's initializer block:
    {
        System.out.println(methodOfY()); // 3rd and 5th point visited: 
                                         // 2220 is printed after
                                         // methodOfY() has been evaluated
    }

    int methodOfY() {
        return m-- + --m; // 4th point visited: m-- = 1111 (m = 1110),
                          // --m = 1109 (m = 1109), therefore 2220 is 
                          // returned
    }
}

public class MainClass {
    public static void main(String[] args) {
        Y y = new Y();
    }
}

现在到前置和后置操作符。您可以“手动”实现它们1以了解它们的行为:

public class PrePostIncrement {
    int value;

    public PrePostIncrement(int value) {
        this.value = value;
    }

    public int preIncrement() {
        this.value = this.value + 1;
        return (this.value);
    }

    public int postIncrement() {
        int result = this.value;
        this.value = this.value + 1;
        return (result);
    }

    public static void main(String... args) {
        PrePostIncrement test = new PrePostIncrement(1111);
        int newValue = test.postIncrement();
        System.out.println(newValue);
    }
}

如您所见,postIncrement() 返回的值是1111。这将是在X 的初始化程序块中分配给m 的新值。因此,m 将具有 1112 的值,只是为了非常简短。

因为我不希望你简单地相信我,所以我写了一些代码来显示这种行为:

public class Main {
    public static volatile int x = 0;
    public static volatile boolean running = true;

    public static void main(String... args) {
        Thread observer = new Thread(() -> {
            while (true) {
                int x = Main.x;
                System.out.println("Observer observed: " + x);
                if (0 != x) {
                    break;
                }
            }
            Main.running = false;
        });
        observer.start();

        while (running) {
            Main.x = Main.x++;
        }
        System.out.println(Main.x);
    }
}

如您所见,observer 的唯一目的是打印x 的值,直到它看到x 的值为1。主线程继续执行x = x++;,直到observer 看到x 的值发生了一次变化。之后,x 的最终值将被打印出来。请注意,此程序必须在真正的多核系统上运行,否则可能会死循环。

在我的系统上,我通常会迭代一到六次 observer,直到看到更改。但是,x 的最终值将始终为“0”2

P.S.:欢迎来到 Java 并发世界。如果要编写一个具有多个线程的应用程序,需要同时访问x,则很可能将x 定义为AtomicInteger 并使用方法int incrementAndGet()(而不是++x)和int getAndIncrement()(而不是++x)。这些方法保证x 的中间值不会被其他线程看到。


1请不要这样做。 ++ii++ 是语言结构,可以由编译器高度优化。这只是为了展示他们的行为。

2即使在多核系统上,该程序也可能在无限循环中运行,但程序将终止almost surely。推理是observer 看到x 的值为0 的概率是

【讨论】:

  • 谢谢@Turing85
猜你喜欢
  • 2022-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多