Java标识符中的“连接字符”是什么?

211

我正在备考SCJP考试,对以下这一句话有疑问:

标识符必须以字母、货币字符($)或连接字符(如下划线_)开头。标识符不能以数字开头!

它说明一个合法的标识符名称可以以连接字符例如下划线开头。但我认为下划线是唯一有效的选项?还有什么其他的连接字符呢?


2
关于“货币字符”:访问此问题的英国访客可能会感到惊讶并有兴趣了解,与能够以“a”货币字符开头一致,Java标识符可以合法地以英镑符号(£)开头。 - 8bitjunkie
11
请注意,自 Java 8 起,下划线(_)被认为是“不建议使用”的标识符。具体来说,编译器会发出以下警告:*(在 Java SE 8 之后的版本中,可能不支持使用“_”作为标识符)*。 - aioobe
4
@aioobe 是的。Brian Goetz 表示他们在将 _ 重新用于未来的语言特性中。以下划线开头的标识符仍然可以使用,但如果用作 lambda 参数名称,则单个下划线是一个错误,其他任何地方使用都会发出警告。 - Boann
1
对于字节码,任何不包含. ; [ / < > :的序列都可以通过:https://dev59.com/F4Tca4cB1Zd3GeqPBfbb https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4 其他所有内容都是Java特有的限制。 - Ciro Santilli OurBigBook.com
@Boann 有趣的是他们不允许在lambda中使用它,但它很可能会作为一个“忽略此参数”的标识符重新出现,并被用在lambda中。我刚试着这样使用:_, _ -> doSomething(); - user31389
7个回答

270

以下是连接单词所使用的字符列表。

http://www.fileformat.info/info/unicode/category/Pc/list.htm

U+005F _ LOW LINE
U+203F ‿ UNDERTIE
U+2040 ⁀ CHARACTER TIE
U+2054 ⁔ INVERTED UNDERTIE
U+FE33 ︳ PRESENTATION FORM FOR VERTICAL LOW LINE
U+FE34 ︴ PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
U+FE4D ﹍ DASHED LOW LINE
U+FE4E ﹎ CENTRELINE LOW LINE
U+FE4F ﹏ WAVY LOW LINE
U+FF3F _ FULLWIDTH LOW LINE

这可以在Java 7上编译。

int _, ‿, ⁀, ⁔, ︳, ︴, ﹍, ﹎, ﹏, _;
一个例子。在这种情况下,tp 是列的名称,给定行的值。
Column<Double> ︴tp︴ = table.getColumn("tp", double.class);

double tp = row.getDouble(︴tp︴);
以下内容:
for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++)
    if (Character.isJavaIdentifierStart(i) && !Character.isAlphabetic(i))
        System.out.print((char) i + " ");
}

打印

$ _ ¢ £ ¤ ¥ ؋ ৲ ৳ ৻ ૱ ௹ ฿ ៛ ‿ ⁀ ⁔ ₠ ₡ ₢ ₣ ₤ ₥ ₦ ₧ ₨ ₩ ₪ ₫ € ₭ ₮ ₯ ₰ ₱ ₲ ₳ ₴ ₵ ₶ ₷ ₸ ₹ ꠸ ﷼ ︳ ︴ ﹍ ﹎ ﹏ ﹩ $ _ ¢ £ ¥ ₩


109
我期待有一天能够继承使用这些标识符的代码! - Marko Topolnik
3
顺便提一下,您也可以使用任何货币符号。 int ৲, ¤, ₪₪₪₪; :D - Peter Lawrey
83
如何翻译:@GrahamBorland How about if( ⁀ ‿ ⁀ == ⁀ ⁔ ⁀) or if ($ == $) or if (¢ + ¢== ₡) or if (B + ︳!= ฿)?@GrahamBorland,以下是您提供的代码片段:
  • if( ⁀ ‿ ⁀ == ⁀ ⁔ ⁀)
  • if ($ == $)
  • if (¢ + ¢== ₡)
  • if (B + ︳!= ฿)
我会将其翻译为:
  • 如果( ⁀ ‿ ⁀ == ⁀ ⁔ ⁀),则执行某些操作。
  • 如果($ == $),则执行某些操作。
  • 如果(¢ + ¢== ₡),则执行某些操作。
  • 如果(B + ︳!= ฿),则执行某些操作。
请注意,这只是简单的直译,可能需要根据上下文进行修改。
- Peter Lawrey
3
这是德拉克马货币符号。目前没有国家使用它,但如果欧洲发生最坏的情况,它可能会重新出现。详细信息请参阅维基百科希腊德拉克马页面。 - Peter Lawrey
Scalaz 经常使用 KleisliArrow[M[]: Monad]: Arrow[({type λ[α, β]=Kleisli[M, α, β]})#λ] = new Arrow[({type λ[α, β]=Kleisli[M, α, β]})#λ] 和 ☆(f() η)。 - James Moore
3
尝试检查isJavaIdentifierPart而不是isJavaIdentifierStart,会更有趣! - Aleksandr Dubinsky

25
遍历整个65k字符并询问Character.isJavaIdentifierStart(c)。 答案是:"undertie" 十进制 8255

14
在Scala中,我忍不住尝试了以下代码:(1 to 65535).map(_.toChar).filter(Character.isJavaIdentifierStart).size - 运行结果为48529个字符... - Tomasz Nurkiewicz
似乎在65k、12k和8.5k附近有一些字符。 - Markus Mikkolainen
如果你使用“!isLetter”和“!isDigit”,它将不会产生任何结果。 - Markus Mikkolainen
2546+2547至少需要“框线绘制…” - Markus Mikkolainen
3
总计数=90648,但我要到Character.MAX_CODE_POINT,这可能超过了2<<16 - Martijn Courteaux

7

3
我不确定这个回答是否完全回答了(隐含的)问题,即哪些字符可以作为Java标识符的开头。通过点击链接,我们最终找到[Character.isJavaIdentifierStart()] (http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Character.html#isJavaIdentifierStart%28char%29) ,其中指出:“如果以下条件之一成立,则字符可以作为Java标识符的开头:...ch是货币符号(例如“ $”); ch是连接标点符号(例如“_”)。 ” - user
1
似乎规范将可接受字符的最终列表留给实现,因此它对于每个人可能是不同的。 - Greg Hewgill
3
考虑到其他内容都有非常严格的规定,这样做是愚蠢的。我认为这些实际上是Unicode字符类,这些类别在(哪里?)Unicode标准中定义。 isJavaIdentifierStart()提到了getType(),货币符号和连接器标点也都是该函数可以返回的类型之一,因此列表可能在其中给出。 "通用类别"实际上是Unicode标准中的一个专用术语。因此,有效的值将是L [所有]、NlScPc - Random832
3
@GregHewgill 是正确的。规范简洁明了,由Character.isJavaIdentifierStart()和Character.isJavaIdentifierPart()定义。重要的是要记住Unicode在不断发展;不要陷入将字符集视为完成的陷阱(拉丁语是一个糟糕的例子;忽略它)。新字符一直在被创建。问问你的日本朋友就知道了。预计合法的Java标识符会随着时间而变化 - 这是有意的。关键是让人们用人类语言编写代码。这导致允许变化的硬性要求。 - James Moore

6

这里是Unicode中连接符号的列表。你无法在键盘上找到它们。

U+005F 下划线 _
U+203F 下波浪线 ‿
U+2040 字符连结线 ⁀
U+2054 反转下波浪线 ⁔
U+FE33 竖排低线 ︳
U+FE34 竖排波浪低线 ︴
U+FE4D 虚线下划线 ﹍
U+FE4E 中央虚线下划线 ﹎
U+FE4F 波浪虚线下划线 ﹏
U+FF3F 全角下划线 _


5
我不知道你使用的键盘布局是什么,但我可以轻松地输入 _ (U+005F) :)。 - bdonlan

4

一个连接字符用于连接两个字符。

在Java中,连接字符是指Character.getType(int codePoint)/Character.getType(char ch)返回值等于Character.CONNECTOR_PUNCTUATION的字符。

请注意,在Java中,字符信息基于Unicode标准,该标准通过将连接字符分配给一般类别Pc(Connector_Punctuation的别名)来识别它们。

以下代码片段:

for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++) {
    if (Character.getType(i) == Character.CONNECTOR_PUNCTUATION
            && Character.isJavaIdentifierStart(i)) {
        System.out.println("character: " + String.valueOf(Character.toChars(i))
                + ", codepoint: " + i + ", hexcode: " + Integer.toHexString(i));
    }
}

打印出可以用于在jdk1.6.0_45上启动标识符的连接字符。

character: _, codepoint: 95, hexcode: 5f
character: ‿, codepoint: 8255, hexcode: 203f
character: ⁀, codepoint: 8256, hexcode: 2040
character: ⁔, codepoint: 8276, hexcode: 2054
character: ・, codepoint: 12539, hexcode: 30fb
character: ︳, codepoint: 65075, hexcode: fe33
character: ︴, codepoint: 65076, hexcode: fe34
character: ﹍, codepoint: 65101, hexcode: fe4d
character: ﹎, codepoint: 65102, hexcode: fe4e
character: ﹏, codepoint: 65103, hexcode: fe4f
character: _, codepoint: 65343, hexcode: ff3f
character: ・, codepoint: 65381, hexcode: ff65

以下可以在jdk1.6.0_45上编译通过,

int _, ‿, ⁀, ⁔, ・, ︳, ︴, ﹍, ﹎, ﹏, _, ・ = 0;

显然,上述声明在jdk1.7.0_80和jdk1.8.0_51上由于以下两个连接字符而无法编译(向后兼容...哎呀!!!)

character: ・, codepoint: 12539, hexcode: 30fb
character: ・, codepoint: 65381, hexcode: ff65

无论如何,细节先放一边,这次考试仅关注基本的拉丁字符集。
此外,在 Java 中用于法定标识符的规范在这里提供。使用 Character 类 API 以获取更多详细信息。

2
Java标识符中允许使用的最有趣的字符之一(但不能以此作为起始字符)是Unicode字符“零宽度非连接符”(&zwnj;,U+200C,参见https://en.wikipedia.org/wiki/Zero-width_non-joiner)。
我曾经在XML片段中使用过这个字符,该片段包含一个引用另一个XML片段的属性值。由于ZWNJ是“零宽度”的,因此无法看到它(除非光标沿着它走,在前一个字符上显示)。它也无法在日志文件和/或控制台输出中看到。但它一直存在:复制并粘贴到搜索字段中会得到它,因此找不到所需的位置。然而,将(可见部分的)字符串键入搜索字段可以找到所需的位置。花了我一段时间才弄清楚这一点。
在使用欧洲键盘布局时,输入零宽度非连接符实际上相当容易(太容易了),至少在其德语变体中,例如“Europatastatur 2.02” - 可以通过AltGr +“.”来实现,这两个键在大多数键盘上都紧挨着,很容易意外按到。
回到Java:我想,你可以编写如下代码:
void foo() {
    int i = 1;
    int i‌ = 2;
}

用零宽不连字符将第二个i添加到代码中(在stackoverflow的编辑器中无法执行此操作),但是没有起作用。IntelliJ(16.3.3)没有抱怨,但是JavaC(Java 8)则抱怨已定义的标识符 - 看起来JavaC实际上允许使用ZWNJ字符作为标识符的一部分,但是当使用反射查看其功能时,ZWNJ字符会被剥离标识符 - 而像‿这样的字符则不会。

0

你可以在标识符中使用的字符列表(而不仅仅是在开头)更加有趣:内部

for (int i = Character.MIN_CODE_POINT; i <= Character.MAX_CODE_POINT; i++)
    if (Character.isJavaIdentifierPart(i) && !Character.isAlphabetic(i))
        System.out.print((char) i + " ");

列表如下:
I wanted to post the output, but it's forbidden by the SO spam filter. That's how fun it is!

它包含大多数控制字符!我的意思是铃声和其他东西!您可以使源代码响起fn铃声!或者使用仅在某些情况下显示的字符,例如软连字号。


它包括 \u007f,即DEL字符。 :-( - Todd O'Bryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接