为什么这个写回来的代码打印出“Hello World!”

这里是我在互联网上find的一些代码:

class M‮{public static void main(String[]a‭){System.out.print(new char[] {'H','e','l','l','o',' ','W','o','r','l','d','!'});}} 

此代码打印Hello World! 在屏幕上; 你可以看到它在这里运行。 我可以清楚地看到public static void main写入,但它是倒退。 这个代码是如何工作的? 这甚至如何编译?

编辑:我在IntellIJ中试过这个代码,它工作正常。 但是,由于某些原因,它不能在记事本++,以及cmd中工作。 我还没有find解决办法,所以如果有人的话,请在下面留言。

这里有不可见的字符,改变代码的显示方式。 在Intellij中,可以通过将代码复制粘贴到空string( "" )中来find这些string,该string用Unicode转义符replace它们,移除它们的效果并显示编译器看到的顺序。

这是复制粘贴的输出:

 "class M\u202E{public static void main(String[]a\u202D){System.out.print(new char[]\n"+ "{'H','e','l','l','o',' ','W','o','r','l','d','!'});}} " 

源代码字符按这个顺序存储,编译器将它们按照这个顺序处理,但是它们的显示方式不同。

请注意\u202E字符,这是一个从右到左的覆盖,从所有字符被迫从右到左显示的块开始,而从左到右覆盖的\u202D开始所有字符被强制为从左到右顺序的嵌套块,覆盖第一个覆盖。

Ergo,当它显示原始代码时, class M正常显示,但是\u202E反转了从那里到\u202D的所有内容的显示顺序。 (从forms上来说,从\u202D到行结束符的所有内容都被颠倒了两次,一次是由于\u202D\u202D一次是由于\u202D而使其余的文本颠倒过来,这就是为什么这个文本出现在行的中间而不是结束)。下一行的方向性由于行结束符而独立于第一行进行处理,所以{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}正常显示。

对于完整的(非常复杂的,几十页长的)Unicode双向algorithm,请参见Unicode标准附录#9 。

它看起来不同,因为Unicode双向algorithm 。 Unicode双向algorithm使用两个不可见的RLO和LRO字符来改变这两个元字符之间嵌套的字符的可视外观

结果是,它们在视觉上看起来相反,但是内存中的实际字符不会颠倒。 你可以在这里分析结果。 Java编译器将忽略RLO和LRO,并将它们视为空白,这就是代码编译的原因。

注1:该algorithm被文本编辑器和浏览器用来同时在一起显示LTR字符(英文)和RTL字符(例如阿拉伯文,希伯来文)的字符 – 因此“bi”是双向的。 您可以在Unicode的网站上阅读更多有关双向algorithm的信息 。
注2:algorithm的第2.2节定义了LRO和RLO的确切行为。

字符U+202Ereflection从右到左的代码,但它是非常聪明的。 隐藏在M开始,

 "class M\u202E{..." 

我是如何find这背后的魔力的

那么,起初当我看到这个问题时我很难,“这是一种笑话,失去了别人的时间”,但是之后,我打开了我的IDE(“IntelliJ”),创build了一个类,并且通过了代码。 编译 !!! 于是,我更好地看了一眼,看到“公共静态空白”是落后的,所以我用光标去了那里, 擦了几个字 ……然后会发生什么? 字符开始向后擦除 ,所以,我想 – 嗯…罕见…我必须执行它…所以我继续执行程序,但首先我需要保存它 …那是当我find了! 。 我无法保存文件,因为我的IDE说,有一个字符有不同的编码, 并指出我在哪里 ,所以我开始在谷歌研究特殊的字符,可以做的工作,就是这样:)

一点关于

Unicode双向algorithm和U+202E有关,简要说明一下 :

Unicode标准规定了一种称为逻辑顺序的内存表示顺序。 当文本以水平线显示时,大多数脚本从左向右显示字符。 但是,有几个脚本(如阿拉伯语或希伯来语),其中显示的水平文本的自然顺序是从右到左。 如果所有文本都具有统一的水平方向,则显示文本的顺序是明确的。

但是,由于这些从右到左的脚本使用从左到右书写的数字,所以文本实际上是双向的:从右到左和从左到右的文本的混合。 除了数字之外,英文和其他脚本的embedded式单词也是从左到右书写的,也产生双向文本。 没有明确的说明,当文本的水平方向不一致时,在确定所显示的字符的sorting时可能出现模糊。

本附录介绍用于确定双向Unicode文本的方向性的algorithm。 该algorithm扩展了当前许多现有实现使用的隐式模型,并为特殊情况添加了显式格式化字符。 在大多数情况下,不需要在文本中包含附加信息以获得正确的显示顺序。

然而,在双向文本的情况下,隐式双向sorting不足以产生可理解的文本。 为了处理这些情况,定义了一组最小的方向格式字符来控制字符在渲染时的sorting。 这样可以精确控制显示顺序以便于清晰的交换,并确保用于简单项目(如文件名或标签)的纯文本始终可以正确sorting显示。

为什么要创build这样的algorithm?

bidialgorithm可以从右向左渲染一系列阿拉伯文或希伯来文字符。

PS:我知道这不是最好的答案,但是先解决问题很有趣

语言规范的第3章详细描述了如何为Java程序完成词汇翻译。 这个问题最重要的是:

程序是用Unicode(§3.1)编写的 ,但是提供了词汇翻译(§3.2) ,所以Unicode转义(§3.3)可以用来包含任何只使用ASCII字符的Unicode字符。

所以一个程序是用Unicode字符编写的,如果文件编码不支持Unicode字符,作者可以使用\uxxxx转义它们,在这种情况下,它被转换成适当的字符。 这种情况下的Unicode字符之一是\u202E 。 它没有直观的显示在代码片段中,但是如果您尝试切换浏览器的编码,隐藏字符可能会出现。

因此,词汇翻译导致了类的声明:

 class M\u202E{ 

这意味着类标识符是M\u202E 。 规范认为这是一个有效的标识符:

 Identifier: IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral IdentifierChars: JavaLetter {JavaLetterOrDigit} 

“Java字母或数字”是Character.isJavaIdentifierPart(int)方法返回true的Character.isJavaIdentifierPart(int)