我们知道它可以在javascript中使用。
但是在Java下面给出的条件下打印"成功"消息是可能的吗?
1 2 3
| if (a ==1 && a ==2 && a ==3) {
System. out. println("Success");
} |
有人建议:
1 2 3 4 5 6
| int _a = 1;
int a = 2;
int a_ = 3;
if (_a == 1 && a == 2 && a_ == 3) {
System. out. println("Success");
} |
号
但是通过这样做,我们改变了实际的变量。还有别的办法吗?
- &&是逻辑and运算符,这意味着a应同时具有值1、2和3,这在逻辑上是不可能的。答案是不,不可能。是否要编写一个if语句来检查a是否具有值1、2或3中的一个值?
- 任何变量都不能同时保存两个不同的值。因此,a==1 && a==2的表达总是对false进行评估。
- 我就知道这就是我问的原因
- 请注意:这个问题可能来自于javascript的同一个问题:stackoverflow.com/questions/48270127/…,在这种情况下,答案是yes。
- 使用int[]数组。
- ArthurAttout不是一个复制品,这个问题是针对JavaScript,而不是Java。
- @不,答案仍然有效
- 在变量名中使用空白的那一个也在Java中工作。当然,这与使用_基本相同,只是你看不到。
- 在两种情况下,一种可以采用a和西里尔字母a;а。
- @请参见"正确",但不一定同时对if(a==1 && a==2 && a==3)进行评估。这可以用来使这项工作,而不必诉诸于Unicode技巧。
- 提醒我写一个程序,使2+2=5
- @托马斯韦勒:是的,这是(因为它几乎是任何语言),见stackoverflow.com/a/48301772/40347。对于所有语言,我们不需要将此作为单独的问题,因为基础问题是相同的。
- @phflack:这个高尔夫答案有趣的是,它的670个赞成票是,2 + 2 == 5仍然是false。
- 在Java中有什么类似Perl 6的连接吗?在Perl6中,您可以编写$a = any(1,2,3);,然后$a将同时表示所有三个值:$a == 1 && $a==2 && $a==3。
- 我已经在这里发布了这个问题的一个PHP衍生产品。做$a == 1 && $a == 2 && $a == 3似乎非常简单。
- 与Javascript版本相比,一个很大的区别是Javascript是如何单线程的,为了获得成功,你或多或少得故意打破一些东西。在Java中,作为一个多线程的环境,在这样的场景中成功的可能性是你必须敏锐地意识到并故意设计的,否则事情就会破裂。
- 每个人都说这不可能是错误的;看看最重要的答案。@OP:请阅读最上面的答案;它很好地回答了你的问题,我建议你接受它。
- 有一段时间,当我在hnq列表上看到这个问题时,我以为这是前面的JS问题。这让我想知道其他20多个答案在哪里哈哈
- 我要投票结束这个问题,因为它毫无意义
- 标准漏洞关闭。
是的,如果将变量a声明为volatile,那么使用多个线程很容易实现这一点。
一个线程不断地将变量A从1更改为3,另一个线程不断地测试该变量。它经常发生,足以在控制台上打印出连续的"成功"流。
(注意,如果您添加一个else {System.out.println("Failure");}子句,您将看到测试失败的次数远远超过它成功的次数。)
实际上,它也可以在不声明a为易失性的情况下工作,但在我的MacBook上只有21次。在没有volatile的情况下,编译器或热点可以缓存a或用if (false)替换if语句。最有可能的是,Hotspot会在一段时间后启动,并将其编译为汇编指令,以缓存a的值。有了volatile,它将永远保持打印"成功"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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();
}
} |
- 这是一个很好的例子!我想下次我和一个初级开发人员讨论潜在的危险并发问题时,我会窃取它:)
- 没有volatile的情况不太可能发生,但原则上,没有volatile关键字的情况下甚至可能发生这种情况,而且这种情况可能发生任意次数,而目前还没有任何保证,即使是volatile也不会发生这种情况。但是,当然,让它在实践中发生,是安静的令人印象深刻…
- @Alexprus我只是在所有开发人员身上做的,而不仅仅是我公司的下级(我知道这是可能的),87%的回答失败成功。
- @霍尔格是真的,没有任何保证。在一个典型的多个全核或多CPU架构中,两个线程都被分配到一个单独的核心,但很可能会发生在volatile上。为实现volatile而创建的内存屏障降低了线程的速度,使线程更可能在短时间内同步运行。这种情况发生的频率远远超过我的预期。它对时间非常敏感,但我大致看到,在(a == 1 && a == 2 && a == 3)的评估中,0.2%到0.8%之间返回true。
- 这是唯一一个真正使预期代码返回真值的答案,而不仅仅是做出相同的微小变化。+ 1
- 为什么匿名类仍然可以访问变量a,而a既不是最终的,也不是有效的最终的?如果我错了,请纠正我,您的代码似乎与Java 8文档相矛盾。
- @dkonayuki a是一个字段,而不是局部变量。"最终"和"有效最终"规则只适用于局部变量。
使用优秀的代码高尔夫答案中的概念(和代码),Integer值可能会受到干扰。
在这种情况下,它可以使int铸造给Integer是相等的,当它们通常不是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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的演员阵容),但仍然有一些有趣的恶作剧发生。
- 非常有趣。我在玩弄虐待以东。这是一个遗憾的演员,但它仍然很酷。
- @迈克尔,我不太明白如何摆脱演员阵容。设置为1/2/3的a将满足a == 1的要求,但不会反过来。
- 几天前,我在JS/Ruby/python和Java中回答了这个问题。我能找到的最难看的版本是使用a.equals(1) && a.equals(2) && a.equals(3),这迫使1、2和3被自动装箱为Integer。
- @埃里克杜明,这可能会让一些乐趣消失,因为如果你让a成为boolean equals(int i){return true;}的一个班级呢?
- 正如我所说,它是最不难看的,但它仍然不是最佳的。由于Integer a = 1;之前在线,很明显a实际上是一个整数。
在这个问题中,AIOOBE建议(并且反对)使用C预处理器来处理Java类。
虽然这是非常欺骗,但这是我的解决方案:
1 2 3 4 5 6 7 8 9
| #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:
1
| cpp -P src/Main.java Main.java && javac Main.java && java Main |
- 这种感觉就像在实际编译代码之前通过查找和替换来运行代码,但是我可以肯定地看到有人在工作流程中做类似的事情。
- @phflack我喜欢它,可以说这个问题是关于在阅读代码时做出假设的危险性。很容易假定a是一个变量,但它可以是语言中任何带有预处理器的任意代码。
- 它不是Java。它是"经过C预处理器后的Java"语言。这个答案使用了类似的漏洞。(注:对于代码高尔夫来说,现在的话题已不再是"欠交"了)
正如我们已经知道的,由于Erwin Bolwidt和Phflack给出了很好的答案,这段代码可能会被认为是正确的,我想说明在处理一个看起来像问题中出现的情况时,你需要保持高度的关注,有时你会看到可能不是你想的那样。
这是我试图证明此代码将Success!打印到控制台。我知道我有点作弊,但我仍然认为这是一个很好的地方来展示它。
不管这样编写代码的目的是什么——最好知道如何处理以下情况,以及如何检查您所看到的内容是否正确。
我用的是西里尔字母"A",它是拉丁字母"A"的一个独特的字符。您可以在这里检查if语句中使用的字符。
这是因为变量的名称来自不同的字母。它们是不同的标识符,创建两个不同的变量,每个变量的值都不同。
请注意,如果希望此代码正常工作,则需要将字符编码更改为同时支持这两个字符的编码,例如所有Unicode编码(UTF-8、UTF-16(Be或Le)、UTF-32、甚至UTF-7)或Windows-1251、ISO 8859-5、Koi8-R(谢谢您-Thomas Weller和PA?Lo Ebermann-指出来):
1 2 3 4 5 6 7 8 9
| public class A {
public static void main (String[] args ) {
int а = 0;
int a = 1;
if(а == 0 && a == 1) {
System. out. println("Success!");
}
}
} |
(我希望你以后任何时候都不必处理这种问题。)
- @迈克尔,你什么意思?它不好出现?我已经把它写在手机上了,在这里,即使切换到桌面视图,我也觉得没问题。如果它能让我的答案更好,请随意编辑它,然后澄清,因为我觉得现在有点困难。
- 希望能更清楚些:)
- @迈克尔非常感谢你。我认为我在回答结束时承认的就足够了:)
- @Mohsenmadi是的,我不能在手机上查到,但我很确定在编辑之前,最后一句话是关于在我的示例中使用不同语言的:p
- 为什么==0与a==1类似?
- @托马斯韦勒,如果我把编码改成UTF-8的信息包括在内,它会满足你的期望吗?
- 不,谢谢。___
- @托马斯韦勒,我编辑了我的答案,让它更清楚。谢谢你指出。
- 更吹毛求疵的是:你不一定需要UTF-8(尽管无论如何都是推荐的),任何既有а又有a的字符编码都可以工作,只要你告诉你的编辑它是用什么编码的(也可能是编译器)。所有Unicode编码都可以工作(UTF-8、UTF-16(Be或Le)、UTF-32,甚至是UTF-7),例如Windows-1251、ISO 8859-5、Koi8-R。
- 我已经遇到那个问题了。我的一个cas(派生出6)有&181的同义词mu(也就是说,如果我输入mu并让它简化表达式,mu就变成&181;)。然而,有人搞砸了,简化的mu和alt gr&181;是不同的字母…
- @sudix是的,有两个不同的字符,U+03BC(μ,希腊小写字母mu)和U+00B5(µ,微符号,来自iso-latin-1的遗产)。Unicode认为许多字符应该是相同的单个字符,但它们作为不同字符存在,只是为了与(从和到的无损转换)遗留字符集兼容。
有另一种方法可以解决这个问题(除了我之前发布的易变数据竞速方法之外),使用powermock的强大功能。PowerMock允许用其他实现替换方法。当与自动拆箱结合时,可以不经修改而使原来的表达式(a == 1 && a == 2 && a == 3)变为真。
@ PHFLAK的答案依赖于修改使用EDOCX1 1调用的Java中的自动装箱过程。下面的方法依赖于通过更改Integer.intValue()调用来修改自动取消装箱。
下面的方法的优点是,使用问题中的op给出的原始if语句是不变的,我认为这是最优雅的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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());
}
}
} |
- powermock是否可以取代合成方法,如用于读取内/外类的private字段的access$nnn方法?这将允许一些其他有趣的变体(甚至与int变量一起使用)。
- @霍尔格有趣的想法,我明白你的意思。它还没起作用-不清楚是什么阻止它起作用。
- 很好,这正是我要做的,但我发现,如果不操纵字节码,从不可变类更改静态方法将非常困难。似乎我错了,但从来没有听说过PowerMock,我想知道它是怎么做到的。另一方面,phflack的答案并不依赖于自动氧化:他更改了缓存整数的地址(因此==实际上是比较整数对象地址而不是值)。
- "asoub铸造((Integer)2)的国际性成坑。更多的反射,它看起来像它是不是不可能的,这可能与拆箱使用反射,但可能与仪器代替(或powermock,这个答案)
- 它可能不是"Holger合成方法上,虽然它让我到寄存器和置换的方法是access$0的存在是在登记检查。但是永远不会被替换。
由于这似乎是一个后续的JavaScript问题,值得注意的是,这个伎俩和类似的工作也在Java:
1 2 3 4 5 6 7 8 9 10
| 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所能做的最糟糕的事情。使用作为有效标识符部分的空白字符或控制字符,或者使用外观相同的不同字母,仍然会创建不同的标识符,并且可以被发现,例如在进行文本搜索时。
但是这个节目
1 2 3 4 5 6 7 8 9
| public class Q48383521 {
public static void main (String[] args ) {
int a ? = 1;
int ? = 2;
if(a ? == 1 && ? == 2) {
System. out. println("success");
}
}
} |
使用两个相同的标识符,至少从Unicode的角度来看是相同的。他们只是使用不同的方法来编码同一个字符?,使用U+00E4和U+0061 U+0308。
论伊迪翁
因此,根据您使用的工具的不同,它们可能不仅看起来相同,启用Unicode的文本工具甚至可能不会报告任何差异,在搜索时总是同时查找这两者。您甚至可能会遇到这样的问题:当将源代码复制给其他人时,不同的表示会丢失,可能会试图为"奇怪的行为"寻求帮助,从而使帮助者无法复制源代码。
- 嗯,这个答案不足以涵盖Unicode滥用吗?
- 是故意的吗?因为,我看到周围都是非常奇怪的代码
- @Michael不完全是这样,因为这个答案是关于使用两个不同的字母(在搜索a时,ides会考虑不同的字母),而这是关于空白的,而且,这也归功于有关javascript问题的更老的答案。请注意,我的评论甚至比Java问题还要老(几天)…
- @拉维:是的,这是基于我以前对类似javascript问题的评论。
- 哇,我从没想过有黑魔法。
- 我看不出区别。这两个答案都提供了一个解决方案,其中if语句中的三个标识符在视觉上相似,但由于使用了异常的Unicode字符,技术上不同。不管是空格还是西里尔字母都无关紧要。
- @迈克尔,你可以那样看,由你决定。正如我所说的,我想说五天前JavaScript给出的答案也适用于Java,只是考虑了问题的历史。是的,对于另一个不涉及链接的javascript问题的答案来说,这有点多余。无论如何,同时我更新了我的答案,增加了一个案例,它不是关于视觉上相似的字符,而是不同的编码方式,同一个字符,甚至不是一个"不寻常的Unicode字符";这是我每天使用的字符…
受@欧文优秀答案的启发,我编写了一个类似的例子,但是使用Java流API。
有趣的是,我的解决方案是可行的,但在极少数情况下(因为just-in-time编译器优化了这样的代码)。
诀窍是使用以下VM选项禁用任何JIT优化:
在这种情况下,成功案例的数量显著增加。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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在多个线程之间共享,而不进行任何同步,这就是为什么结果是不确定的。
沿着类似的线,通过用一个大数除(或乘)来强制一个浮点(或双)下溢(或溢出):
1 2 3 4 5 6
| 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");
} |
号
- "这是一patrickroberts特异性行为的每underflow浮点的Java文档。也看到这个,这个,这个和这个。