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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48383521/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:49:31  来源:igfitidea点击:

Can (a==1 && a==2 && a==3) evaluate to true in Java?

java

提问by Taylor Sen

We know it can in JavaScript.

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

But is it possible to print "Success" message on the condition given below in Java?

但是是否可以在下面给出的条件下用 Java 打印“成功”消息?

if (a==1 && a==2 && a==3) {
    System.out.println("Success");
}

Someone suggested:

有人建议:

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

But by doing this we are changing the actual variable. Is there any other way?

但是通过这样做,我们正在改变实际的变量。有没有其他办法?

回答by Erwin Bolwidt

Yes, it's quite easy to achieve this with multiple threads, if you declare variable aas volatile.

是的,如果您将变量声明a为 volatile ,则使用多线程很容易实现这一点。

One thread constantly changes variable a from 1 to 3, and another thread constantly tests that a == 1 && a == 2 && a == 3. It happens often enough to have a continuous stream of "Success" printed on the console.

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

(Note if you add an else {System.out.println("Failure");}clause, you'll see that the test fails far more often than it succeeds.)

(请注意,如果添加else {System.out.println("Failure");}子句,您会看到测试失败的次数远远多于成功的次数。)

In practice, it also works without declaring aas volatile, but only 21 times on my MacBook. Without volatile, the compiler or HotSpot is allowed to cache aor replace the ifstatement with if (false). Most likely, HotSpot kicks in after a while and compiles it to assembly instructions that do cache the value of a. Withvolatile, it keeps printing "Success" forever.

实际上,它也可以在不声明a为 volatile 的情况下工作,但在我的 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();
    }
}

回答by Przemys?aw Moskal

As we already know that it is possibleto make this code evaluate to true thanks to great answers of Erwin Bolwidtand phflack, I wanted to show that you need to keep a high level of attention when dealing with a condition that looks like the one presented in the question, as sometimes what you see might not be exactly what you think it is.

正如我们已经知道,由于Erwin Bolwidtphflack 的出色回答,可以使此代码评估为真,我想表明您在处理看起来像所呈现的情况时需要保持高度关注在问题中,因为有时您看到的可能与您认为的不完全一样。

This is my attempt to show that this code prints Success!to the console. I know I cheated a bit, but I still think this is a good place to present it right here.

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

No matter what the purposes of writing code like this are - better to know how to deal with the following situation and how to check if you're not wrong with what you think you see.

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

I used the Cyrillic 'a' which is a distinct character from the latin 'a'. You can inspect the characters used in the if statement here.

我使用了西里尔字母“a”,它与拉丁字母“a”截然不同。您可以在此处检查 if 语句中使用的字符。

This works because the names of the variables are taken from different alphabets. They are distinct identifiers, creating two distinct variables with a different value in each.

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

Note that if you want this code to work properly, character encoding needs to be changed to one supporting both characters, e.g. all Unicode encodings (UTF-8, UTF-16 (in BE or LE), UTF-32, even UTF-7), or Windows-1251, ISO 8859-5, KOI8-R (thank you - Thomas Wellerand Pa?lo Ebermann- for pointing it out):

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

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

(I hope you will never have to deal with that sort of problem any time in the future.)

(我希望你以后任何时候都不必处理那种问题。)

回答by phflack

Using concepts (and code) from a brilliant code golf answer, Integervalues can be messed with.

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

In this case, it can make ints casted to Integers be equal when they wouldn't normally be:

在这种情况下,它可以使ints 转换为Integers 时它们通常不会相等:

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

Unfortunately it's not quite as elegant as Erwin Bolwidt's multithreaded answer(as this one requires Integercasting), but still some fun shenanigans take place.

不幸的是,它不像Erwin Bolwidt 的多线程答案那么优雅(因为这个Integer答案需要转换),但仍然会发生一些有趣的恶作剧。

回答by Holger

Since this seems to be a follow-up of this JavaScript question, it's worth noting that this trickand similar works in Java too:

由于这似乎是this JavaScript question的后续,值得注意的是,这个技巧和类似的东西在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");
        }
    }
}

On Ideone

在 Ideone 上



But note that this isn't the worst thing you could do with Unicode. Using white-space or control characters that are valid identifiers parts or using different letters that look the samestill creates identifiers that are different and can be spotted, e.g. when doing a text search.

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

But this program

但是这个节目

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

uses two identifiers that are the same, at least from the Unicode point of view. They just use different ways to encode the same character ?, using U+00E4and U+0061 U+0308.

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

On Ideone

在 Ideone 上

So depending on the tool you're using, they may not only look the same, Unicode enabled text tools may not even report any difference, always finding both when searching. You may even have the problem that the different representations get lost when copying the source code to someone else, perhaps trying to get help for the “weird behavior”, making it non-reproducible for the helper.

因此,根据您使用的工具,它们可能不仅看起来相同,启用 Unicode 的文本工具甚至可能不会报告任何差异,在搜索时总是找到两者。您甚至可能会遇到这样的问题,即在将源代码复制给其他人时,不同的表示会丢失,也许是为了“奇怪的行为”寻求帮助,使其无法被帮助者复制。

回答by Pado

In this question@aioobe suggests (and advise against) the use of C preprocessor for Java classes.

这个问题中,@aioobe 建议(并反对)对 Java 类使用 C 预处理器。

Although it is extremelycheaty, that's my solution:

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

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

If executed using the following commands it will output exactlyone Success:

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

cpp -P src/Main.java Main.java && javac Main.java && java Main

回答by Erwin Bolwidt

There is another way to approach this (in additional to the volatile data-racing approach that I posted earlier), using the power of PowerMock. PowerMock allows methods to be replaced with other implementations. When that is combined with auto-unboxing, the original expression (a == 1 && a == 2 && a == 3), without modification, can be made true.

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

@phflack's answer relies on modifying the auto-boxing process in Java that uses the Integer.valueOf(...)call. The below approach relies on modifying auto-unboxing by changed the Integer.intValue()call.

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

The advantage of the below approach is that the original if-statement given by the OP in the question is used unchanged, which I think is the most elegant.

以下方法的优点是问题中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());
        }
    }

}

回答by Aloke

Along similar lines, by forcing a float (or double) to underflow (or overflow) through division (or multiplication) by a large number:

沿着类似的路线,通过除法(或乘法)以大数强制浮点数(或双精度数)下溢(或上溢):

int a = 1;
if (a / Float.POSITIVE_INFINITY == 1 / Float.POSITIVE_INFINITY
        && a / Float.POSITIVE_INFINITY == 2 / Float.POSITIVE_INFINITY
        && a / Float.POSITIVE_INFINITY == 3 / Float.POSITIVE_INFINITY) {
    System.out.println("Success");
}

回答by Oleksandr Pyrohov

Inspired by the @Erwin's excellent answer, I wrote a similar example, but using Java Stream API.

受到@Erwin 出色回答的启发,我编写了一个类似的示例,但使用的是 Java Stream API

And an interesting thing is that my solution works, but in very rare cases (because  just-in-timecompiler optimizes such a code).

有趣的是,我的解决方案有效,但在极少数情况下(因为  just-in-time编译器优化了这样的代码)。

The trick is to disable any JIToptimizations using the following VMoption:

诀窍是JIT使用以下VM选项禁用任何优化:

-Djava.compiler=NONE

In this situation, the number of success cases increases significantly. Here is the code:

在这种情况下,成功案例的数量会显着增加。这是代码:

class Race {
    private static int a;

    public static void main(String[] args) {
        IntStream.range(0, 100_000).parallel().forEach(i -> {
            a = 1;
            a = 2;
            a = 3;
            testValue();
        });
    }

    private static void testValue() {
        if (a == 1 && a == 2 && a == 3) {
            System.out.println("Success");
        }
    }
}

P.S. Parallel streams use ForkJoinPoolunder the hood, and variable ais shared between multiple threads without any synchronization, that's why the result is non-deterministic.

PS 并行流ForkJoinPool在幕后使用,变量a在多个线程之间共享而没有任何同步,这就是结果不确定的原因。