当前位置: 首页 > 面试题库 >

Java为什么在其标识符中允许使用控制字符?

洪高刚
2023-03-14
问题内容

在精确地探究Java标识符中允许使用哪些字符时,我偶然发现了一件非常奇怪的事情,以至于几乎可以肯定这是一个错误。

我预料地发现,Java标识符合他们开始与拥有的Unicode字符属性的要求ID_Start,并其次是与物业ID_Continue,以授予领先的下划线和美元符号例外。事实并非如此,我发现与我所听说的那种普通标识符或其他任何想法都存在极大差异。

简短演示

请考虑以下演示,证明Java标识符中允许使用ASCII ESC字符(八进制033):

$ perl -le 'print qq(public class escape { public static void main(String argv[]) { String var_\033 = "i am escape: \033"; System.out.println(var_\033); }})' > escape.java
$ javac escape.java
$ java escape | cat -v
i am escape: ^[

但是,这甚至更糟。实际上,几乎无限恶化。甚至允许NULL!还有成千上万个甚至都不是标识符字符的代码点。我已经在Solaris,Linux和运行Darwin的Mac上进行了测试,所有结果均相同。

长演示

这是一个测试程序,它将显示Java完全允许的所有这些意外代码点,作为合法标识符名称的一部分。

#!/usr/bin/env perl
# 
# test-java-idchars - find which bogus code points Java allows in its identifiers
# 
#   usage: test-java-idchars [low high]
#   e.g.:  test-java-idchars 0 255
#
# Without arguments, tests Unicode code points
# from 0 .. 0x1000.  You may go further with a
# higher explicit argument.
#
# Produces a report at the end.
#
# You can ^C it prematurely to end the program then
# and get a report of its progress up to that point.
#
# Tom Christiansen
# tchrist@perl.com
# Sat Jan 29 10:41:09 MST 2011

use strict;
use warnings;

use encoding "Latin1";
use open IO => ":utf8";

use charnames ();

$| = 1;

my @legal;

my ($start, $stop) = (0, 0x1000);

if (@ARGV != 0) {
    if (@ARGV == 1) {
        for (($stop) = @ARGV) { 
            $_ = oct if /^0/;   # support 0OCTAL, 0xHEX, 0bBINARY
        }
    }
    elsif (@ARGV == 2) {
        for (($start, $stop) = @ARGV) { 
            $_ = oct if /^0/;
        }
    } 
    else {
        die "usage: $0 [ [start] stop ]\n";
    } 
}

for my $cp ( $start .. $stop ) {
    my $char = chr($cp);

    next if $char =~ /[\s\w]/;

    my $type = "?";
    for ($char) {
        $type = "Letter"      if /\pL/;
        $type = "Mark"        if /\pM/;
        $type = "Number"      if /\pN/;
        $type = "Punctuation" if /\pP/;
        $type = "Symbol"      if /\pS/;
        $type = "Separator"   if /\pZ/;
        $type = "Control"     if /\pC/;
    } 
    my $name = $cp ? (charnames::viacode($cp) || "<missing>") : "NULL";
    next if $name eq "<missing>" && $cp > 0xFF;
    my $msg = sprintf("U+%04X %s", $cp, $name);
    print "testing \\p{$type} $msg...";
    open(TESTPROGRAM, ">:utf8", "testchar.java") || die $!;

print TESTPROGRAM <<"End_of_Java_Program";

public class testchar { 
    public static void main(String argv[]) { 
        String var_$char = "variable name ends in $msg";
        System.out.println(var_$char); 
    }
}

End_of_Java_Program

    close(TESTPROGRAM) || die $!;

    system q{
        ( javac -encoding UTF-8 testchar.java \
            && \
          java -Dfile.encoding=UTF-8 testchar | grep variable \
        ) >/dev/null 2>&1
    };

    push @legal, sprintf("U+%04X", $cp) if $? == 0;

    if ($? && $? < 128) {
        print "<interrupted>\n";
        exit;  # from a ^C
    }

    printf "is %s in Java identifiers.\n",  
        ($? == 0) ? uc "legal" : "forbidden";

}

END { 
    print "Legal but evil code points: @legal\n";
}

这是仅在前33个既不是空格也不是标识符字符的代码点上运行该程序的html" target="_blank">示例:

$ perl test-java-idchars 0 0x20
testing \p{Control} U+0000 NULL...is LEGAL in Java identifiers.
testing \p{Control} U+0001 START OF HEADING...is LEGAL in Java identifiers.
testing \p{Control} U+0002 START OF TEXT...is LEGAL in Java identifiers.
testing \p{Control} U+0003 END OF TEXT...is LEGAL in Java identifiers.
testing \p{Control} U+0004 END OF TRANSMISSION...is LEGAL in Java identifiers.
testing \p{Control} U+0005 ENQUIRY...is LEGAL in Java identifiers.
testing \p{Control} U+0006 ACKNOWLEDGE...is LEGAL in Java identifiers.
testing \p{Control} U+0007 BELL...is LEGAL in Java identifiers.
testing \p{Control} U+0008 BACKSPACE...is LEGAL in Java identifiers.
testing \p{Control} U+000B LINE TABULATION...is forbidden in Java identifiers.
testing \p{Control} U+000E SHIFT OUT...is LEGAL in Java identifiers.
testing \p{Control} U+000F SHIFT IN...is LEGAL in Java identifiers.
testing \p{Control} U+0010 DATA LINK ESCAPE...is LEGAL in Java identifiers.
testing \p{Control} U+0011 DEVICE CONTROL ONE...is LEGAL in Java identifiers.
testing \p{Control} U+0012 DEVICE CONTROL TWO...is LEGAL in Java identifiers.
testing \p{Control} U+0013 DEVICE CONTROL THREE...is LEGAL in Java identifiers.
testing \p{Control} U+0014 DEVICE CONTROL FOUR...is LEGAL in Java identifiers.
testing \p{Control} U+0015 NEGATIVE ACKNOWLEDGE...is LEGAL in Java identifiers.
testing \p{Control} U+0016 SYNCHRONOUS IDLE...is LEGAL in Java identifiers.
testing \p{Control} U+0017 END OF TRANSMISSION BLOCK...is LEGAL in Java identifiers.
testing \p{Control} U+0018 CANCEL...is LEGAL in Java identifiers.
testing \p{Control} U+0019 END OF MEDIUM...is LEGAL in Java identifiers.
testing \p{Control} U+001A SUBSTITUTE...is LEGAL in Java identifiers.
testing \p{Control} U+001B ESCAPE...is LEGAL in Java identifiers.
testing \p{Control} U+001C INFORMATION SEPARATOR FOUR...is forbidden in Java identifiers.
testing \p{Control} U+001D INFORMATION SEPARATOR THREE...is forbidden in Java identifiers.
testing \p{Control} U+001E INFORMATION SEPARATOR TWO...is forbidden in Java identifiers.
testing \p{Control} U+001F INFORMATION SEPARATOR ONE...is forbidden in Java identifiers.
Legal but evil code points: U+0000 U+0001 U+0002 U+0003 U+0004 U+0005 U+0006 U+0007 U+0008 U+000E U+000F U+0010 U+0011 U+0012 U+0013 U+0014 U+0015 U+0016 U+0017 U+0018 U+0019 U+001A U+001B

这是另一个演示:

$ perl test-java-idchars 0x600 0x700 | grep -i legal
testing \p{Control} U+0600 ARABIC NUMBER SIGN...is LEGAL in Java identifiers.
testing \p{Control} U+0601 ARABIC SIGN SANAH...is LEGAL in Java identifiers.
testing \p{Control} U+0602 ARABIC FOOTNOTE MARKER...is LEGAL in Java identifiers.
testing \p{Control} U+0603 ARABIC SIGN SAFHA...is LEGAL in Java identifiers.
testing \p{Control} U+06DD ARABIC END OF AYAH...is LEGAL in Java identifiers.
Legal but evil code points: U+0600 U+0601 U+0602 U+0603 U+06DD

问题

任何人都可以解释这种看似疯狂的行为吗?从U +
0000开始,到处都有很多很多其他莫名其妙允许的代码点,这也许是最奇怪的。如果在前0x1000个代码点上运行它,则确实会看到某些模式,例如,使用属性允许任何和所有代码点Current_Symbol。但是,至少我完全无法解释太多其他情况。


问题答案:

在Java语言规范第3.8节推迟到Character.isJavaIdentifierStart()和Character.isJavaIdentifierPart()

。后者以及其他条件具有Character.isIdentifierIgnorable(),它允许使用非空白控制字符(包括整个C1范围,请参阅列表链接)。



 类似资料:
  • 问题内容: CSS标识符和的(完整)有效/允许的 字符集 字符是什么? 是否可以使用正则表达式进行验证?它与浏览器无关吗? 问题答案: 字符集无关紧要。允许的字符更重要。检查CSS规范。这里是相关的: 在CSS中,标识符(包括选择器中的元素名称,类和ID )只能包含字符和ISO 10646 及更高版本的字符,以及连字符()和下划线();它们不能以数字,两个连字符或连字符后跟数字开头。标识符还可以包

  • 问题内容: 我用来解析/验证用户输入。不幸的是,它在解析时允许字符作为后缀。 示例代码: 结果: 我实际上希望他们两个都有解析异常。我怎样才能告诉我不允许输入? 问题答案: 从以下文档中: 从给定字符串的开头解析文本以产生数字。 该方法可能不会使用给定字符串的整个文本。 这是一个示例,可以使您了解如何确保考虑整个字符串。 输出:

  • 问题内容: 我最近了解到,Java源代码中不仅允许将Unicode字符作为Unicode字符(例如),还允许将其作为转义序列(例如)。 第一个变体对我来说很有意义-它允许程序员使用他们选择的国际语言来命名变量和方法。但是,我看不到第二种方法的任何实际应用。 以下是一些使用Java SE 6和NetBeans 6.9.1进行测试的说明用法的代码: 此代码将打印出3.141592653589793 说

  • 问题内容: 以下代码产生输出“ Hello World!”。(不,请尝试)。 原因是Java编译器将Unicode字符解析为新行并转换为: 因此导致评论被“执行”。 由于可以将其用于“隐藏”恶意代码或任何邪恶的程序员可以想到的内容, 因此为什么允许在注释中使用它 ? 为什么Java规范允许这样做? 问题答案: Unicode解码发生在任何其他词汇翻译之前。这样做的主要好处是,它使得在ASCII和任

  • 问题内容: 我认为,由于类型擦除,使用和不允许的除外。为什么Java语言设计人员允许此异常?对于无限制的通配符类型,类型擦除没有任何作用吗? 问题答案: 关键是对象知道其具体类,但不知道其通用类型参数。因此,如果我们构造一个,则在执行时就知道它是 某种 类型的-但它不知道该部分。 “ 某种”部分恰好意味着什么,这就是为什么: 已验证。这等效于使用原始类型:

  • 问题内容: 经过测试,我发现linux允许文件名中的任何字符(和和null()除外)。那么我不应该以什么顺序输入文件名呢?我听说领导可能会混淆某些命令行程序,这对我来说并不重要,但是如果其他人决定收集一堆文件并使用某些GNU程序对其进行过滤,则可能会打扰其他人。 我建议删除前导空格和尾随空格,而我计划这样做的原因仅是因为用户通常并不打算拥有前导/尾随空格。 可能会有什么问题序列,我应该考虑不允许什