但是可以在Java下面给出的条件下打印“成功”消息吗?
KeyError: "['col3' 'col4' 'col5'] not in index"
有人建议:
if (a==1 && a==2 && a==3) {
System.out.println("Success");
}
但通过这样做,我们正在改变实际变量。还有其他办法吗?
答案 0 :(得分:316)
是的,如果你将变量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();
}
}
答案 1 :(得分:83)
使用brilliant code golf answer中的概念(和代码),Integer
值可能会混乱。
在这种情况下,当int
成为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");
}
}
的平均值可能是正常的:
Integer
不幸的是,它并不像Erwin Bolwidt's multithreaded answer 那样优雅(因为这需要{{1}}施放),但仍然会发生一些有趣的恶作剧。
答案 2 :(得分:48)
在this question @aioobe建议(并建议反对)使用C预处理器进行Java类。
虽然它非常非常,但这是我的解决方案:
#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");
}
}
如果使用以下命令执行,它将输出完全一个Success
:
cpp -P src/Main.java Main.java && javac Main.java && java Main
答案 3 :(得分:37)
由于 Erwin Bolwidt 和 phflack 的优秀答案,我们已经知道可能使此代码评估为真,我我想表明你在处理一个看起来像问题中出现的条件时需要保持高度的注意力,因为有时你所看到的可能不是你想象的那样。
这是我尝试显示此代码将Success!
打印到控制台。 我知道我有点作弊,但我仍然认为这是一个在这里展示它的好地方。
无论编写这样的代码的目的是什么 - 更好地了解如何处理以下情况以及如何检查您认为自己看到的内容是否错误。
我使用的是西里尔语'a',这是拉丁语'a'中的一个独特角色。您可以检查if语句here中使用的字符。
这是有效的,因为变量的名称取自不同的字母表。它们是不同的标识符,创建两个不同的变量,每个变量具有不同的值。
请注意,如果您希望此代码正常工作,则需要将字符编码更改为支持两个字符的字符,例如所有Unicode编码(UTF-8,UTF-16(BE或LE),UTF-32,甚至UTF-7),或Windows-1251,ISO 8859-5,KOI8-R(谢谢 - Thomas Weller 和PaŭloEbermann - 指出来了:
public class A {
public static void main(String[] args) {
int а = 0;
int a = 1;
if(а == 0 && a == 1) {
System.out.println("Success!");
}
}
}
(我希望将来任何时候都不会遇到这类问题。)
答案 4 :(得分: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());
}
}
}
答案 5 :(得分:16)
由于这似乎是this JavaScript question的后续行动,因此值得注意this trick以及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");
}
}
}
但请注意,这不是你用Unicode做的最糟糕的事情。使用作为有效标识符部分的空格或控制字符或using different letters that look the same仍会创建不同且可被发现的标识符,例如在进行文本搜索时。
但是这个程序
public class Q48383521 {
public static void main(String[] args) {
int ä = 1;
int ä = 2;
if(ä == 1 && ä == 2) {
System.out.println("success");
}
}
}
使用两个相同的标识符,至少从Unicode的角度来看。他们只是使用ä
和U+00E4
使用不同的方式对同一个字符U+0061 U+0308
进行编码。
因此,根据您使用的工具,它们可能不仅看起来相同,启用Unicode的文本工具甚至可能无法报告任何差异,在搜索时始终同时找到它们。你甚至可能会遇到这样的问题:在将源代码复制给其他人时,不同的表示会丢失,也许是试图获得“怪异行为”的帮助,使得它对帮助者来说是不可重复的。
答案 6 :(得分:3)
受到@Erwin优秀answer的启发,我写了一个类似的例子,但使用了Java Stream API 。
有趣的是我的解决方案有效,但在极少数情况下(因为just-in-time
编译器优化了这样的代码)。
诀窍是使用以下JIT
选项禁用任何VM
优化:
-Djava.compiler=NONE
在这种情况下,成功案例的数量显着增加。这是代码:
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。并行流使用ForkJoinPool
,并且变量 a 在多个线程之间共享而没有任何同步,这就是结果是非确定性的原因。
答案 7 :(得分:1)
沿着similar lines,通过一个大数字强制浮点(或双精度)通过除法(或乘法)下溢(或乘法):
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");
}