开始学习JAVA的词汇结构了。JAVA程序是用unicode来表示的,比如我们常用到的换行符。所有的unicode都会强制转化为一系列的input elements,包括空格,注释,和tokens。JAVA的发展和unicode的发展是密不可分的。unicode的发展我们可以参考它的官方网站:http://www.unicode.org。JAVA编程语言是用UTF-16来描述文本的,只有少数的API类使用UTF-32,JAVA的平台提供了两者之间转化的方法。除了注释,标识符,字符常量,字符串常量,所有的input elements都是ASCII码组成。
Lexical Translations
词汇的翻译我们以后称之为Lexical Translations分为三步:
1、原始的unicode stream所有的转义字符转化为对应的unicode字符。转义字符的形式是/uXXXX,其中XXXX代表的是十六进制的值。用ASCII码字符的程序在这个转化中也是适用的。
2、利用第一步转化来的unicode字符,将其变为input elements和换行符。
3、使用第二步转化来的input elements和换行符,去掉其中的空格、注释,就形成了我在JAVA语言规范学习笔记(2)中提到的tokens,然后利用这些tokens做语法分析。
在Lexical Translations的实现里,首先就必须识别出转义字符,以后我就称它Unicode Escapes,它是用/u后面紧接着是XXXX的UTF-16的十六进制值表示的,将它转化为对应的UTF-16 unicode 字符。而描述那些辅助字符的时候,就需要两个转义符。使用我在JAVA语言规范学习笔记(2)学到的表示形式,那么这一步的转化就可以这样来表示:
UnicodeInputCharacter:
UnicodeEscape
RawInputCharacter
UnicodeEscape:
/ UnicodeMarker HexDigit HexDigit HexDigit HexDigit
UnicodeMarker:
u
UnicodeMarker u
RawInputCharacter:
any Unicode character
HexDigit: one of
0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
在这里,/,u,XXXX,这些都是ASCII码字符。在这个处理过程中,它会考察“/”后面还有多少“/”。XXXX只能为偶数不可以为奇数。比如“//u2297-/2297”这种情况会怎样处理?它处理后会变成“//u2297-”。一个合法的转义字符“/”后面紧接着一个或多个“u”,但是最后一个“u”后面却没有“XXXX”,编译时就会报错。
再看一个“/u005cu005a”,因为“005c”的值是“/”,那么它就变成“/u005a”,而转化后的后续字符串不会再来一次转义的转化了。
JAVA语言中也详细描述了如何将一个用unicode character编写的程序转化为一个用ASCII码字符的程序,这样就方便使用ASCII码字符的工具来处理它。如果你的系统里没有相应的字符,那么实现里就会用“/uXXXX”这种形式来代替。其实,这个里面涉及到的还和一些JAVA的编译法则有关,我们不必去深究编译上到底如何实现,只是利用这些编译上的法则或者说方法来学习JAVA语言的技术细节。
实现下一步通过line terminators来给程序的unicode character形式来分行。line terminators是由JAVA编译器或其他系统组件来处理的。在注释里用“//”的形式来表现的。
LineTerminator:
the ASCII LF
character, also known as "newline"
the ASCII CR
character, also known as "return"
the ASCII CR
character followed by the ASCII LF
character
InputCharacter:
UnicodeInputCharacter but not CR
or LF
这就是我们开始将到的第二步所处理得到的结果。
经过第一步和第二步的处理,我们得到了input character和line terminators,开始第三步的处理了。要将它们变成input element,就是没有空格、注释,只有语法分析才能接受的terminal symbols,叫做tokens。这个过程是这样表示的。
在原始的输入流中,如果“x”是在“y”之前的,在这里我们称作,“x”是在“y”的左边;而“y”是在“x”的右边。因为在输入流中所有的换行符已经被替换为unicode characters了。
而在上面的定义里White Space包括:ASCII码的空格、水平制表符、换页符合换行符。
WhiteSpace:
the ASCII SP character, also known as "space"
the ASCII HT character, also known as "horizontal tab"
the ASCII FF character, also known as "form feed"
LineTerminator
再来看注释的定义:
Comment:
TraditionalComment
EndOfLineComment
TraditionalComment:
/ * CommentTail
EndOfLineComment:
/ / CharactersInLineopt
CommentTail:
* CommentTailStar
NotStar CommentTail
CommentTailStar:
/
* CommentTailStar
NotStarNotSlash CommentTail
NotStar:
InputCharacter but not *
LineTerminator
NotStarNotSlash:
InputCharacter but not * or /
LineTerminator
CharactersInLine:
InputCharacter
CharactersInLine InputCharacter
还要注意一点,注释是不会内嵌的。
标识符,必须由Java Letters和Java Digit组成,不限长度,但开头的必须是Java Letters,它不能是关键字、boolean常量、null常量。定义如下:
Identifier:
IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
JavaLetter
IdentifierChars JavaLetterOrDigit
JavaLetter:
any Unicode character that is a Java letter
JavaLetterOrDigit:
any Unicode character that is a Java letter-or-digit
只要是unicode characters中的字符都可以,甚至如中文、韩文、日文都可以用来做标识符。不信?你可以在你的系统上试试。你可以用Character.isJavaIdentifierStart(int)方法来判断这个字符是否为Java Letters,用Character.isJavaIdentifierPart(int)来判断这个字符是否为Java Letters和Java Digits。如果是,就返回true,否则返回false。Java Letters包括ASCII码中的拉丁字母A-Z和a-z,由于历史上的原因,下划线_和$也是。Java Digits则是ASCII码中的数字0-9。两个标识符相等时,那么它们中的字母和数字所对应的unicode也必须相等。
关键字,这个不必多讲。中间有两个需要提及,const和goto这两个是JAVA中的保留字。
常量的定义:
Literal:
IntegerLiteral
FloatingPointLiteral
BooleanLiteral
CharacterLiteral
StringLiteral
NullLiteral
首先来看IntegerLiteral,可以用十进制、十六进制、八进制三种形式
IntegerLiteral:
DecimalIntegerLiteral
HexIntegerLiteral
OctalIntegerLiteral
DecimalIntegerLiteral:
DecimalNumeral IntegerTypeSuffixopt
HexIntegerLiteral:
HexNumeral IntegerTypeSuffixopt
OctalIntegerLiteral:
OctalNumeral IntegerTypeSuffixopt
IntegerTypeSuffix: one of
l L
Long型的常量后面要加上ASCII码字符的l或L来表示,建议用L,以免和1混淆。
十进制的常量是用ASCII码字符的0~9来表示的,其语法定义如下:
DecimalNumeral:
0
NonZeroDigit Digitsopt
Digits:
Digit
Digits Digit
Digit:
0
NonZeroDigit
NonZeroDigit: one of
1 2 3 4 5 6 7 8 9
十六进制则是用0x或者0X作为前缀,后面是ASCII码字符中数字的0~9和字母的A~Z或a~z来表示,定义如下:
HexNumeral:
0 x HexDigits
0 X HexDigits
HexDigits:
HexDigit
HexDigit HexDigits
HexDigit: one of
0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
八进制的表示是用0作为前缀,后面是ASCII码字符中数字的0~7来表示,定义如下:
OctalNumeral:
0 OctalDigits
OctalDigits:
OctalDigit
OctalDigit OctalDigits
OctalDigit: one of
0 1 2 3 4 5 6 7
每一种整形的常量都有其表示数的范围,如果你使用整型常量赋值时,如果超出了它的范围,那么在编译时就会报错。
浮点型常量是由一个整型部分、一个小数点、一个小数部分、一个指数部分和一个类型的后缀表示的。如果是用十进制的形式来表示,如果有指数部分,那么是在数字后面用一个“e”或者“E”加上一个带符号的数字来表示。而用十六进制表是时,如果有指数部分,那么是在数字后面用一个“p”或者“P”加上一个带符号的数字来表示。
如果浮点类型是float时,那么后缀为ASCII码字符f或F;如果浮点类型是double时,那么后缀为ASCII码字符d或D。定义如下:
FloatingPointLiteral:
DecimalFloatingPointLiteral
HexadecimalFloatingPointLiteral
DecimalFloatingPointLiteral:
Digits . Digitsopt ExponentPartopt FloatTypeSuffixopt
. Digits ExponentPartopt FloatTypeSuffixopt
Digits ExponentPart FloatTypeSuffixopt
Digits ExponentPartopt FloatTypeSuffix
ExponentPart:
ExponentIndicator SignedInteger
ExponentIndicator: one of
e E
SignedInteger:
Signopt Digits
Sign: one of
+ -
FloatTypeSuffix: one of
f F d D
HexadecimalFloatingPointLiteral:
HexSignificand BinaryExponent FloatTypeSuffixopt
HexSignificand:
HexNumeral
HexNumeral .
0x HexDigitsopt . HexDigits
0X HexDigitsopt . HexDigits
BinaryExponent:
BinaryExponentIndicator SignedInteger
BinaryExponentIndicator:one of
p P
无论是float或double类型,它们分别用IEEE 754标准中的32-bit和64-bit二进制格式来表示的。从unicode字符串转化为浮点类型时,使用的是Float和Double这两个类的valueOf()方法,这两个类在java.lang.*这个package里。
同样,浮点类型也是有范围的,如果你使用时超出了它们的范围时,编译器也会报错的。那么如果你想在你的程序里表示无限大,但又不想让编译器报错,那可以使用Float和Double这两个类的POSITIVE_INFINITY和NEGATIVE_INFINITY这两个常量来表示。Float和Double这两个类还有一个预定义的常量NaN,它表示这不是一个数字。
Boolean Literals
boolean类型只有两个常量形式,true和false。定义如下:
BooleanLiteral: one of
true false
Character Literals
字符常量可以用来表示字符或者转义字符,用一对单引号表示‘’。定义形式如下:
CharacterLiteral:
' SingleCharacter '
' EscapeSequence '
SingleCharacter:
InputCharacter but not ' or /
如果字符常量SingleCharacter和EscapeSequence不是以’结尾,那么编译时就会报错;如果行终结符出现在字符常量中的‘之后和’之前,那么编译时就会报错。
String Literals
字符串常量则是由一个或多个字符组成,用“”来表示。其定义如下:
StringLiteral:
" StringCharactersopt "
StringCharacters:
StringCharacter
StringCharacters StringCharacter
StringCharacter:
InputCharacter but not " or /
EscapeSequence
由于unicode的转义在第一步中就完成了,所以在字符串中出现:“/u000a”在第一步中就完成和换行符第二步中也完成了的,是不正确的。但是我们可以用另外的一种形式来代替,比如换行“/n”,回车“/r”。字符串是不变的。
Escape Sequences for Character and String Literals
下面定义的是一些可以在字符常量和字符串常量中使用的转义字符:
EscapeSequence:
/ b /* /u0008: backspace BS */
/ t /* /u0009: horizontal tab HT */
/ n /* /u000a: linefeed LF */
/ f /* /u000c: form feed FF */
/ r /* /u000d: carriage return CR */
/ " /* /u0022: double quote " */
/ ' /* /u0027: single quote ' */
/ / /* /u005c: backslash / */
OctalEscape /* /u0000 to /u00ff: from octal value */
OctalEscape:
/ OctalDigit
/ OctalDigit OctalDigit
/ ZeroToThree OctalDigit OctalDigit
OctalDigit: one of
0 1 2 3 4 5 6 7
ZeroToThree: one of
0 1 2 3
如果在使用时不是按上述的形式来写,编译时就会报错。
The Null Literal
这个定义很简单:
NullLiteral:
null
Separators
分隔符的定义:
Separator: one of
( ) { } [ ] ; , .
Operators
37个运算符的定义:
Operator: one of
= > < ! ~ ? :
== <= >= != && || ++ --
+ - * / & | ^ % << >> >>>
+= -= *= /= &= |= ^= %= <<= >>= >>>=
至此,JAVA中的词汇结构就到这里给出了一个全貌。