Как переменная Java может отличаться от самой себя?

Мне интересно, можно ли решить этот вопрос на 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");
        }
    }
}

В моей лаборатории я получил следующий вопрос: как вы можете пропустить первый случай (т.е. сделать условие x == x ложным) без изменения самого условия?

Ответ 1

Один простой способ - использовать Float.NaN:

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

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

Вы можете сделать то же самое с Double.NaN.


Из JLS §15.21.1. Операторы числового равенства == и !=:

Тестирование равенства с плавающей точкой выполняется в соответствии с правилами стандарта IEEE 754:

  • Если либо операнд NaN, то результат == равен false, но результат != равен true.

    Действительно, тест x!=x равен true тогда и только тогда, когда значение x равно NaN.

...

Ответ 2

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

Ответ 3

В Спецификации языка Java NaN не равно NaN.

Поэтому любая строка, которая вызвала бы x равным NaN, вызовет это, например

double x=Math.sqrt(-1);

Из спецификации языка Java:

Операторы с плавающей запятой не производят исключений (§11). Операция что переполнение создает подписанную бесконечность, операцию, которая underflows производит денормализованное значение или подписанный ноль, а операция, которая не имеет математически определенного результата, дает NaN. Все в результате числовые операции с NaN в качестве операнда производят NaN. В виде уже описано, NaN неупорядочен, поэтому числовое сравнение операция с участием одного или двух NaN возвращает false и любой != сравнение с использованием NaN возвращает true, включая x!= x, когда x является NaN.

Ответ 4

Не уверен, что это опция, но изменение 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

Ответ 5

Замененная строка может быть прочитана.

double x = Double.NaN;

Это приведет к печати печатаемой страницы.

Спецификация языка Java (JLS) говорит:

Операторы с плавающей запятой не производят исключений (§11). Операция, которая переполняет, создает подписанную бесконечность, операция, которая выполняется под потоком, создает денормализованное значение или подписанный нуль, а операция, не имеющая математически определенного результата, дает NaN. В результате числовые операции с NaN в качестве операнда дают NaN. Как уже было описано, NaN неупорядочен, поэтому операция числового сравнения с одним или двумя NaN возвращает false, а любое!= Сравнение с NaN возвращает true, включая x!= X, когда x является NaN.

Ответ 6

Мне удалось получить 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;
    }
  }
}

Ответ 7

Существует так много решений:

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");
        }
    }
}

Ответ 8

Одно из простых решений:

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

Но я не знаю всех правил этой загадки...

:) Я знаю, что это обман, но, не зная всех правил, это самое легкое решение вопроса:)

Ответ 9

Создайте свой собственный класс System в том же пакете с Condition.
В этом случае ваш класс System скроет java.lang.System class

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");
        }
    }
}  

Идеальный 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");
        }
    }
}