可以(a == 1 && a == 2 && a == 3)在Java中评估为true?

Tay*_*Sen 171 java

我们知道它可以在JavaScript中使用.

但是有可能在Java下面给出的条件下打印"成功"消息吗?

if (a==1 && a==2 && a==3) {
    System.out.println("Success");
}
Run Code Online (Sandbox Code Playgroud)

有人建议:

int _a = 1;
int a  = 2;
int a_ = 3;
if (_a == 1 && a == 2 && a_ == 3) {
    System.out.println("Success");
}
Run Code Online (Sandbox Code Playgroud)

但通过这样做,我们正在改变实际变量.还有其他方法吗?

Erw*_*idt 320

是的,如果你将变量声明a为volatile ,那么使用多个线程很容易实现这一点.

一个线程不断地将变量a从1更改为3,另一个线程不断地测试它a == 1 && a == 2 && a == 3.它经常发生在控制台上印有连续的"成功"流.

(注意,如果添加一个else {System.out.println("Failure");}子句,您将看到测试失败的次数远远超过成功.)

在实践中,它也可以在没有声明a为易失性的情况下工作,但在我的MacBook上只有21次.如果没有volatile,则允许编译器或HotSpot缓存a或替换该if语句if (false).最有可能的是,HotSpot会在一段时间后启动并将其编译为汇编指令,这些指令会缓存值a.随着 volatile它,它永远印刷"成功".

public class VolatileRace {
    private volatile int a;

    public void start() {
        new Thread(this::test).start();
        new Thread(this::change).start();
    }

    public void test() {
        while (true) {
            if (a == 1 && a == 2 && a == 3) {
                System.out.println("Success");
            }
        }
    }

    public void change() {
        while (true) {
            for (int i = 1; i < 4; i++) {
                a = i;
            }
        }
    }

    public static void main(String[] args) {
        new VolatileRace().start();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的例子!我想我下次与初级开发人员讨论潜在危险的并发问题时会偷走它:) (82认同)
  • 没有`volatile`它不太可能,但原则上,这甚至可以在没有`volatile`关键字的情况下发生,它可能会发生任意次数,而在手上,没有保证它会发生,甚至用`volatile`.但当然,在实践中发生这种情况,令人印象深刻...... (6认同)
  • @AlexPruss我刚刚在所有开发者身上做过,而不仅仅是我公司的三年级学生(我*知道*它可能),答案中失败的成功率为87% (5认同)
  • 这是唯一能够使目标代码返回true的答案,而不仅仅是制作相同的小变体.+1 (4认同)
  • @Holger True,也不保证.在典型的多全核或多CPU架构中,两个线程都被分配到一个单独的核心,很可能它会发生在`volatile`中.为实现"volatile"而创建的内存屏障会降低线程的速度,并使它们更有可能在短时间内同步运行.它发生的次数远远超出我的预期.它对时间敏感,但我大致看到`(a == 1 && a == 2 && a == 3)`返回'true`的评估的0.2%到0.8%之间. (3认同)

phf*_*ack 84

使用来自精彩代码高尔夫答案的概念(和代码),Integer可以混淆值.

在这种情况下,当它们通常不是这样时,它可以使ints Integer成为平等:

import java.lang.reflect.Field;

public class Test
{
    public static void main(String[] args) throws Exception
    {
        Class cache = Integer.class.getDeclaredClasses()[0];
        Field c = cache.getDeclaredField("cache");
        c.setAccessible(true);
        Integer[] array = (Integer[]) c.get(cache);
        // array[129] is 1
        array[130] = array[129]; // Set 2 to be 1
        array[131] = array[129]; // Set 3 to be 1

        Integer a = 1;
        if(a == (Integer)1 && a == (Integer)2 && a == (Integer)3)
            System.out.println("Success");
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,它并不像Erwin Bolwidt的多线程答案 那样优雅(因为这需要Integer演员),但仍然会发生一些有趣的恶作剧.

  • 我几天前用JS/Ruby/Python和[`Java`](/sf/answers/3380899581/)回答了这个问题.我能找到的最难看的版本是使用`a.equals(1)&& a.equals(2)&& a.equals(3)`,强制`1`,`2`和`3`自动装箱为`Integer`s. (4认同)

Pad*_*ado 49

这个问题中, @ oooobe建议(并建议不要)在Java类中使用C预处理器.

虽然它非常讨厌,但这是我的解决方案:

#define a evil++

public class Main {
    public static void main(String[] args) {
        int evil = 1;
        if (a==1 && a==2 && a==3)
            System.out.println("Success");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果使用下面的命令执行时会输出恰好一个Success:

cpp -P src/Main.java Main.java && javac Main.java && java Main
Run Code Online (Sandbox Code Playgroud)

  • 这种感觉就像在实际编译之前通过查找和替换来运行代码,但我绝对可以看到有人在做他们工作流程的一部分 (6认同)
  • 这不是Java.它是"通过C预处理器后的Java"语言.[this](https://codegolf.stackexchange.com/a/34992/69850)使用的类似漏洞答案.(注意:[tag:underhanded]现在是代码高尔夫的**主题**) (2认同)

Prz*_*kal 38

正如我们已经知道的那样,由于Erwin Bolwidtphflack的很好的答案,有可能使这个代码评估为真,我想表明在处理看起来像一个呈现的条件时你需要保持高水平的注意力在这个问题中,有时你所看到的可能并不完全是你认为的那样.

这是我尝试显示此代码打印Success!到控制台.我知道我有点作弊,但我仍然认为这是一个在这里展示它的好地方.

无论编写这样的代码的目的是什么 - 更好地知道如何处理以下情况以及如何检查您认为自己看到的内容是否错误.

我使用了西里尔语'a',这是拉丁语'a'中的一个独特角色.您可以在此处检查if语句中使用的字符.

这是有效的,因为变量的名称取自不同的字母表.它们是不同的标识符,创建两个不同的变量,每个变量具有不同的值.

请注意,如果您希望此代码正常工作,则需要将字符编码更改为支持两个字符的编码,例如所有Unicode编码(UTF-8,UTF-16(BE或LE),UTF-32,甚至UTF-7) ),或Windows-1251,ISO 8859-5,KOI8-R(谢谢你 - Thomas WellerPaŭloEbermann - 指出来):

public class A {
    public static void main(String[] args) {
        int ? = 0;
        int a = 1;
        if(? == 0 && a == 1) {
            System.out.println("Success!");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(我希望将来任何时候都不会遇到这类问题.)


Erw*_*idt 26

使用PowerMock的强大功能还有另一种方法可以解决这个问题(除了我之前发布的易失性数据竞争方法).PowerMock允许将方法替换为其他实现.当它与自动拆箱结合使用时,可以使原始表达式(a == 1 && a == 2 && a == 3)无需修改即可.

@phflack的答案依赖于修改使用该Integer.valueOf(...)调用的Java中的自动装箱过程.以下方法依赖于通过更改Integer.intValue()呼叫来修改自动拆箱.

下面方法的优点是OP在问题中给出的原始if语句没有改变,我认为这是最优雅的.

import static org.powermock.api.support.membermodification.MemberMatcher.method;
import static org.powermock.api.support.membermodification.MemberModifier.replace;

import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@PrepareForTest(Integer.class)
@RunWith(PowerMockRunner.class)
public class Ais123 {
    @Before
    public void before() {
        // "value" is just a place to store an incrementing integer
        AtomicInteger value = new AtomicInteger(1);
        replace(method(Integer.class, "intValue"))
            .with((proxy, method, args) -> value.getAndIncrement());
    }

    @Test
    public void test() {
        Integer a = 1;

        if (a == 1 && a == 2 && a == 3) {
            System.out.println("Success");
        } else {
            Assert.fail("(a == 1 && a == 2 && a == 3) != true, a = " + a.intValue());
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

  • @Asoub的转换(`(Integer)2`)框为int.[更多考虑反思](/sf/answers/1117684291/),看起来使用反射拆箱无法做到这一点,但可以使用[Instrumentation](https:/ /stackoverflow.com/a/15967008/5475891)而不是_(或与PowerMock一样,在此答案中)_ (2认同)

Hol*_*ger 17

由于这似乎是这个JavaScript问题的后续,因此值得注意的是,这个技巧和类似的工作也在Java中:

public class Q48383521 {
    public static void main(String[] args) {
        int a? = 1;
        int ?2 = 3;
        int a = 3;
        if(a?==1 && a==?2 && a==3) {
            System.out.println("success");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

关于Ideone


但请注意,这不是你用Unicode做的最糟糕的事情.使用作为有效标识符部分的白色空格或控制字符或使用看起来相同的不同字母仍会创建不同且可被发现的标识符,例如在进行文本搜索时.

但这个计划

public class Q48383521 {
    public static void main(String[] args) {
        int a? = 1;
        int ä = 2;
        if(a? == 1 && ä == 2) {
            System.out.println("success");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用两个相同的标识符,至少从Unicode的角度来看.他们只是使用不同的方法来编码相同的字符ä,使用U+00E4U+0061 U+0308.

关于Ideone

因此,根据您使用的工具,它们可能不仅看起来相同,启用Unicode的文本工具甚至可能不会报告任何差异,在搜索时始终找到两者.在将源代码复制到其他人时,您甚至可能会遇到不同表示形式丢失的问题,可能试图获取"奇怪行为"的帮助,使其对助手不可重现.

  • 嗯,不是[这个答案](/sf/answers/3386983281/)足够覆盖unicode滥用? (3认同)
  • `intㅤ2 = 3;`是故意的吗?因为,我看到了非常奇怪的代码 (2认同)
  • 我没有看到这种区别.这两个答案都提供了一种解决方案,其中if语句中的三个标识符在视觉上相似但在技术上因使用不常见的unicode字符而不同.无论是空白还是西里尔都是无关紧要的. (2认同)
  • @Michael你可能会这样看,这取决于你.如上所述,我想说五天前JavaScript的答案也适用于Java,只考虑问题的历史.是的,这对于另一个没有引用链接JavaScript问题的答案来说有点多余.无论如何,同时我更新了我的答案,添加了一个与视觉上不相似的字符不同的情况,但不同的方法来编码*相同的字符*并且它甚至不是"不寻常的unicode字符"; 这是我每天都在使用的角色...... (2认同)