以下代码生成输出“Hello World!” (不,真的,试试看)。
public static void main(String... args) { // The comment below is not a typo. // \u000d System.out.println("Hello World!"); }
这样做的原因是 Java 编译器将 Unicode 字符解析\u000d为新行并转换为:
\u000d
public static void main(String... args) { // The comment below is not a typo. // System.out.println("Hello World!"); }
从而导致评论被“执行”。
既然这可以用来“隐藏”恶意代码或邪恶的程序员可以想到的任何东西, 为什么评论中允许它 呢?
为什么 Java 规范允许这样做?
Unicode 解码发生在任何其他词法翻译之前。这样做的主要好处是它使在 ASCII 和任何其他编码之间来回切换变得微不足道。您甚至不需要弄清楚评论的开始和结束位置!
如JLS 第 3.3 节所述,这允许任何基于 ASCII 的工具处理源文件:
[…] Java 编程语言指定了一种将 Unicode 编写的程序转换为 ASCII 的标准方法,该方法将程序更改为可以由基于 ASCII 的工具处理的形式。[…]
这为平台独立性(支持的字符集的独立性)提供了基本保证,这一直是 Java 平台的关键目标。
能够在文件的任何位置写入任何 Unicode 字符是一个简洁的功能,在使用非拉丁语言记录代码时,在注释中尤其重要。它可以以如此微妙的方式干扰语义的事实只是一个(不幸的)副作用。
这个主题有很多陷阱,Joshua Bloch 和 Neal Gafter 的 Java Puzzlers 包括以下变体:
这是一个合法的Java程序吗?如果是这样,它会打印什么? \u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020 \u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079 \u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020 \u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063 \u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028 \u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020 \u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b \u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074 \u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020 \u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b \u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
这是一个合法的Java程序吗?如果是这样,它会打印什么?
\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020 \u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079 \u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020 \u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063 \u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028 \u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020 \u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b \u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074 \u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020 \u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b \u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
(这个程序原来是一个简单的“Hello World”程序。)
在解谜者的解决方案中,他们指出了以下几点:
更严重的是,这个难题有助于强化前三个的教训: 当您需要在程序中插入可以以任何其他方式表示的字符时,Unicode 转义是必不可少的。 在所有其他情况下避免使用它们。
资料来源:Java:在评论中执行代码?!