Java变量如何与自身不同?

109

我在想这个问题是否可以用Java解决(我对这门语言不熟悉)。以下是代码:

class Condition {
    // you can change in the main
    public static void main(String[] args) { 
        int x = 0;
        if (x == x) {
            System.out.println("Ok");
        } else {
            System.out.println("Not ok");
        }
    }
}

我在实验室收到了以下问题:

如何在不修改条件的情况下打印“Not ok”?

编辑

如何跳过第一个情况(即使x == x条件为假)而不修改条件本身?


12
我认为应该有更多限制,否则太过开放。 - Fermat's Little Student
52
是不是只需要这样简单的一行代码:System.out.println("Gotcha!");,而不用注释呢? :) - stuXnet
7
好的,那么 double a = Double.NaN 是最简短的答案,而我的“hack”只是一种欺骗。 - Christian Kuetbach
50
这是有趣的Java小知识,但我希望没有人会考虑将其作为面试题。雇用候选人的人应该尽力弄清楚候选人是否理解编程,而不是他积累了多少琐事。在我17年的Java编程中,我几乎没有使用过浮点数,更不用说NaN构造了,更不用说它与==操作符的行为如何了... - arcy
8
个人观点而已,我个人讨厌任何重载基本运算符的程序,并且很高兴我收到的任何Java代码都没有被搞乱(我曾看到*被重载为向量叉乘点积,因为两者都是向量乘法的一种;真是令人恼火!而在Java中,一个称为.cross(),另一个称为.dot(),没有混淆。此外,“重载==运算符并始终返回false”的事情也不会发生,这似乎是Java的优点。 - Richard Tingle
显示剩余18条评论
11个回答

177

一种简单的方法是使用Float.NaN

float x = Float.NaN;  // <--

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
不行

你可以使用Double.NaN实现同样的效果。


根据JLS §15.21.1. 数值等号运算符==!=

浮点数的等式测试遵循IEEE 754标准的规则:

  • 如果任一操作数为NaN,则==的结果为false,但!=的结果为true

    事实上,当且仅当x的值为NaN时,测试x!=x的结果是true

...


160
int x = 0;
if (x == x) {
    System.out.println("Not ok");
} else {
    System.out.println("Ok");
}

65
好的,这句话完全回答了所问的问题。 - Dave Newton
5
没错,但如果我们按照这个回答完全按字面意义理解问题,那么我们可以完全删除"else"。这在技术上并不违反问题的条款。 - arshajii
70
考虑到这是我第二高投票的答案,我不确定是该得出我很有趣还是我是个烂程序员的结论。 - Jeroen Vannevel
5
考虑到要求,我认为这是最简单/只做必要的/最合适的答案 ;) - Izkata
12
显然这是事后编辑过的。原来的问题是“如何打印'not ok'”。 - Jeroen Vannevel
显示剩余5条评论

148

根据Java语言规范NaN不等于NaN

因此,任何导致x等于NaN的代码行都会导致这种情况,例如:

double x=Math.sqrt(-1);

从Java语言规范中得知:

浮点运算符不会产生异常 (§11)。溢出操作会产生带符号的无穷大,下溢操作会产生一个非规格化值或带符号的零,而没有数学确定结果的操作则会产生NaN。所有以NaN作为操作数的数值运算都将产生NaN作为结果。正如已经描述过的那样,NaN是无序的,所以涉及一个或两个NaN的数值比较运算返回false,任何包括 NaN 的 != 比较都返回 true,包括 x!=x 当 x 是 NaN 时。


@sᴜʀᴇsʜᴀᴛᴛᴀ 说得很好,我太忙于去找所谓的“编码约定”,忘记了实际上回答问题。 - Richard Tingle
只有在a声明为Object或double时才有效。 - Christian Kuetbach
1
@ChristianKuetbach 对的,在没有任何相反的信息的情况下,我假设被注释掉的那一行可以是任何东西。 - Richard Tingle
2
即使我的答案是欺骗的,但它仍然正确并符合规则。我只在if语句之前进行了编辑,并且只打印出“Gotcha!”。我确信,这个答案不是这个谜题创造者心中的答案。但是这个谜题没有很好地定义(就像大多数软件项目一样)。 - Christian Kuetbach

73

不确定是否可行,但将x从局部变量更改为字段,可以允许其他线程在if语句的左右两侧读取其值之间更改其值。

这里是一个简短的演示:

class Test {

    static int x = 0;

    public static void main(String[] args) throws Exception {

        Thread t = new Thread(new Change());
        t.setDaemon(true);
        t.start();

        while (true) {
            if (x == x) {
                System.out.println("Ok");
            } else {
                System.out.println("Not ok");
                break;
            }
        }
    }
}

class Change implements Runnable {
    public void run() {
        while (true)
            Test.x++;
    }
}

输出:

⋮
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok

8
哈哈,努力加一分,但是啊...没有人在的Java实验室会想出这样的东西(包括教练)。 - William Gaul
28
@WilliamGaul 真的吗?我一直认为这是一个基本示例,展示了多线程可能存在的问题,以及为什么认为这个主题很容易的人不应该负责任何事情 :) - Pshemo

56
替换后的文本可以这样写。
double x = Double.NaN;

这将导致 gotcha 被打印。

Java 语言规范 (JLS) 表示:

浮点数运算不会产生异常 (§11)。溢出的操作会生成一个有符号的无穷大,下溢的操作会生成一个非规格化值或者是有符号的零,并且没有数学定义结果的操作会生成 NaN。所有带有 NaN 作为操作数的数字操作都会生成 NaN 作为结果。如已经描述的那样,NaN 是无序的,因此涉及一个或两个 NaN 的数字比较操作返回 false,任何涉及 NaN 的 != 比较都返回 true,包括当 x 为 NaN 时的 x!=x。


如果声明a为String a = "Nope",则会导致编译错误。这就是我询问'a'的类型的原因。 - Christian Kuetbach
上面的代码没有提供任何关于类型的信息,因此我做出了一个假设,即a尚未定义。 - Mex
我认为你的答案就是谜题创造者心中的答案。但规则并不清晰。只有两条规则:1. 只能在带有注释的行中插入代码;2. 只能输出一个“Gotcha!”。 - Christian Kuetbach
谜题的作者可以通过在代码周围加上 { } 来使其始终有效。 - Mex

30

我从这里抓到了一个漏洞:Gotcha!

volatile Object a = new Object();

class Flipper implements Runnable {
  Object b = new Object();

  public void run() {
    while (true)  {
      Object olda = a;
      a = b;
      a = olda;
    }
  }

}

public void test() {
  new Thread(new Flipper()).start();

  boolean gotcha = false;
  while (!gotcha) {
    // I've added everything above this - I would therefore say still legal.
    if (a == a) {
      System.out.println("Not yet...");
    } else {
      System.out.println("Gotcha!");
      // Uncomment this line when testing or you'll never terminate.
      //gotcha = true;
    }
  }
}

1
这不仅改变了评论,还改变了问题的要求。 - Mex
我尝试过类似的东西,但我认为它保证总是会产生相同的结果,不是吗? - AndreDurao
4
@Mex - 的确如此,但它保留了足够多的原始信息来证明另一个关键点:有时候 a != a 是因为它被另一个线程改变了。我猜这在面试中会得到加分。 - OldCurmudgeon
这实际上相当聪明,我认为这是通过“希望”第一个线程在比较之间更改a来工作的。 - Richard Tingle
4
关于您的怀疑; 值得注意的是,您在if语句关键部分上方写的所有内容如果必要的话都可以写成一行可怕的代码。 - Richard Tingle
显示剩余5条评论

27

有很多解决方案:

import java.io.PrintStream;

class A extends PrintStream {
    public A(PrintStream x) {
        super(x);
    }

    public void println(String x) {
        super.println("Not ok");
    }

    public static void main(String[] args) {
        System.setOut(new A(System.out));
        int x = 0;
        if (x == x) {
            System.out.println("Ok");
        } else {
            System.out.println("Not ok");
        }
    }
}

1
除非我漏掉了什么,否则super.println应该是“不行”的,对吧? - Izkata
@Izkata 是的,没有重新检查所需的输出应该是什么。 - Johannes Kuhn

26

一个简单的解决方案是:

System.out.println("Gotcha!");if(false)
if( a == a ){
  System.out.println("Not yet...");
} else {
  System.out.println("Gotcha!");
}

但我不知道这个谜语的所有规则...

:) 我知道这是一种欺骗,但是如果不知道所有规则,这是否是问题的最简单解决方法:)


1
可以一行代码实现: - Christian Kuetbach
6
@ChristianKuetbach 与所有程序一样。 - Richard Tingle
2
@ChristianKuetbach 公平起见,如果您真的尝试像那样编程,编译器应立即删除计算机上的所有文件。 - Richard Tingle
1
为什么要这么难?if (System.out.println("Gotcha") && false) - alexis
3
你的代码无法编译,因为在 if 语句中,不能使用 'void' 类型。具体来说,在 if 语句的条件表达式中,不能使用 System.out.println("Gotcha") 这个语句,因为它返回的是 void 类型。 - Christian Kuetbach
显示剩余5条评论

14
在相同的包中创建您自己的System类,与Condition类位于同一包中。
在这种情况下,您的System类将隐藏java.lang.System类。
class Condition
{
    static class System
    {
        static class out
        {
            static void println(String ignored)
            {
                java.lang.System.out.println("Not ok");
            }
        }
    }

    public static void main (String[] args) throws java.lang.Exception
    {
        int x = 0;
        if (x == x) 
        {
           System.out.println("Not ok");
        } 
        else 
        {
           System.out.println("Ok");
        }
    }
}  

Ideone DEMO


10

使用另一篇回答中相同的跳过/更改输出方法:

class Condition {
    public static void main(String[] args) {
        try {
            int x = 1 / 0;
            if (x == x) {
                System.out.println("Ok");
            } else {
                System.out.println("Not ok");
            }
        } catch (Exception e) {
            System.out.println("Not ok");
        }
    }
}

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