Scanner类中的nextToken()方法解读
下面看一下nextToken()方法的源码实现。
1、Java中的控制字符
- case ' ': // (Spec 3.6)
- case '\t': // (Spec 3.6)
- case FF: // (Spec 3.6) 换页符 换页字符
- do {
- scanChar(); // 操作的是bufferpointer指针的值
- } while (ch == ' ' || ch == '\t' || ch == FF);
- endPos = bufpointer;
- processWhiteSpace();
- break;
- case LF: // (Spec 3.4)
- scanChar();
- endPos = bufpointer;
- processLineTerminator();
- break;
- case CR: // (Spec 3.4) \r
- scanChar();
- if (ch == LF) { // \n
- scanChar();
- }
- endPos = bufpointer;
- processLineTerminator();
- break;
关于 LF CR 参考https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.4
关于FF 或者\t 等参考https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.6
2、Java标识符
有如下规定:
(1)标识符是由字母、数字、下划线、美元($)符号组成的
(2)不能以数字开头
(3)不能是java中的关键字
(4)可以用中文,不会报错,但最好不要用中文
实现代码:
- case 'A': case 'B': case 'C': case 'D': case 'E':
- case 'F': case 'G': case 'H': case 'I': case 'J':
- case 'K': case 'L': case 'M': case 'N': case 'O':
- case 'P': case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X': case 'Y':
- case 'Z':
- case 'a': case 'b': case 'c': case 'd': case 'e':
- case 'f': case 'g': case 'h': case 'i': case 'j':
- case 'k': case 'l': case 'm': case 'n': case 'o':
- case 'p': case 'q': case 'r': case 's': case 't':
- case 'u': case 'v': case 'w': case 'x': case 'y':
- case 'z':
- case '$': case '_':
- scanIdent();
- return;
3、数字的表示
- case '0':
- scanChar();
- if (ch == 'x' || ch == 'X') { // 例如int x = 0x101
- scanChar();
- skipIllegalUnderscores();
- if (ch == '.') {
- scanHexFractionAndSuffix(false);
- } else if (digit(16) < 0) {
- lexError("invalid.hex.number");
- } else {
- scanNumber(16);
- }
- } else if (ch == 'b' || ch == 'B') { // 例如int x = 0b101
- // java7的新特性二进制字面量
- if (!allowBinaryLiterals) {
- // source {0} 中不支持二进制文字\n(请使用 -source 7 或更高版本以启用二进制文字)
- lexError("unsupported.binary.lit", source.name);
- allowBinaryLiterals = true;
- }
- scanChar();
- skipIllegalUnderscores();
- if (digit(2) < 0) {
- // 二进制数字中必须包含至少一个二进制数
- lexError("invalid.binary.number");
- } else {
- scanNumber(2);
- }
- } else {
- putChar('0');
- if (ch == '_') {
- int savePos = bufpointer;
- do {
- scanChar();
- } while (ch == '_');
- if (digit(10) < 0) {
- // 非法下划线
- lexError(savePos, "illegal.underscore");
- }
- }
- scanNumber(8);
- }
- return;
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- scanNumber(10);
- return;
- case '.':
- scanChar();
- if ('0' <= ch && ch <= '9') {
- putChar('.');
- scanFractionAndSuffix();
- } else if (ch == '.') {
- putChar('.'); putChar('.');
- scanChar();
- if (ch == '.') {
- scanChar();
- putChar('.');
- token = ELLIPSIS;
- } else {
- lexError("malformed.fp.lit");
- }
- } else {
- token = DOT;
- }
- return;
4、斜杠
- case '/':
- scanChar();
- if (ch == '/') {
- do {
- scanCommentChar();
- } while (ch != CR && ch != LF && bufpointer < buflen);
- if (bufpointer < buflen) {
- endPos = bufpointer;
- processComment(CommentStyle.LINE);
- }
- break;
- } else if (ch == '*') { // 处理文档注释
- scanChar();
- CommentStyle style;
- if (ch == '*') {
- style = CommentStyle.JAVADOC;
- scanDocComment();
- } else {
- style = CommentStyle.BLOCK;
- while (bufpointer < buflen) {
- if (ch == '*') {
- scanChar();
- if (ch == '/') break;
- } else {
- scanCommentChar();
- }
- }
- }
- if (ch == '/') {
- scanChar();
- endPos = bufpointer;
- processComment(style);
- break;
- } else {
- lexError("unclosed.comment");
- return;
- }
- } else if (ch == '=') {
- name = names.slashequals;
- token = SLASHEQ;
- scanChar();
- } else {
- name = names.slash;
- token = SLASH;
- }
- return;
5、反斜杠
- case '\'':
- scanChar();
- if (ch == '\'') {
- lexError("empty.char.lit");
- } else {
- if (ch == CR || ch == LF)
- lexError(pos, "illegal.line.end.in.char.lit");
- scanLitChar();
- if (ch == '\'') {
- scanChar();
- token = CHARLITERAL;
- } else {
- lexError(pos, "unclosed.char.lit");
- }
- }
- return;
6、双引号
- case '\"':
- scanChar();
- while (ch != '\"' && ch != CR && ch != LF && bufpointer < buflen)
- scanLitChar();
- if (ch == '\"') {
- token = STRINGLITERAL;
- scanChar();
- } else {
- lexError(pos, "unclosed.str.lit");
- }
- return;
7、默认处理
在Java中,哪些字符组合成为一个Token是通过调用nextToken方法实现的,每调用一次方法就会构造一个Token,而这些Token必然是com.sun.tools.javac.parser.Token中的任何元素之一。其定义如下:
- /** An interface that defines codes for Java source tokens
- * returned from lexical analysis.
- */
- public enum Token implements Formattable {
- EOF,
- ERROR,
- IDENTIFIER, // 如类名、包名、变量名、方法名等
- ABSTRACT("abstract"),
- ASSERT("assert"),
- BOOLEAN("boolean"),
- BREAK("break"),
- BYTE("byte"),
- CASE("case"),
- CATCH("catch"),
- CHAR("char"),
- CLASS("class"),
- CONST("const"),
- CONTINUE("continue"),
- DEFAULT("default"),
- DO("do"),
- DOUBLE("double"),
- ELSE("else"),
- ENUM("enum"),
- EXTENDS("extends"),
- FINAL("final"),
- FINALLY("finally"),
- FLOAT("float"),
- FOR("for"),
- GOTO("goto"),
- IF("if"),
- IMPLEMENTS("implements"),
- IMPORT("import"),
- INSTANCEOF("instanceof"),
- INT("int"),
- INTERFACE("interface"),
- LONG("long"),
- NATIVE("native"),
- NEW("new"),
- PACKAGE("package"),
- PRIVATE("private"),
- PROTECTED("protected"),
- PUBLIC("public"),
- RETURN("return"),
- SHORT("short"),
- STATIC("static"),
- STRICTFP("strictfp"),
- SUPER("super"),
- SWITCH("switch"),
- SYNCHRONIZED("synchronized"),
- THIS("this"),
- THROW("throw"),
- THROWS("throws"),
- TRANSIENT("transient"),
- TRY("try"),
- VOID("void"),
- VOLATILE("volatile"),
- WHILE("while"),
- INTLITERAL,
- LONGLITERAL,
- FLOATLITERAL,
- DOUBLELITERAL,
- CHARLITERAL,
- STRINGLITERAL,
- TRUE("true"),
- FALSE("false"),
- NULL("null"),
- LPAREN("("),
- RPAREN(")"),
- LBRACE("{"),
- RBRACE("}"),
- LBRACKET("["),
- RBRACKET("]"),
- SEMI(";"),
- COMMA(","),
- DOT("."),
- ELLIPSIS("..."),
- EQ("="),
- GT(">"),
- LT("<"),
- BANG("!"),
- TILDE("~"),
- QUES("?"),
- COLON(":"),
- EQEQ("=="),
- LTEQ("<="),
- GTEQ(">="),
- BANGEQ("!="),
- AMPAMP("&&"),
- BARBAR("||"),
- PLUSPLUS("++"),
- SUBSUB("--"),
- PLUS("+"),
- SUB("-"),
- STAR("*"),
- SLASH("/"),
- AMP("&"),
- BAR("|"),
- CARET("^"),
- PERCENT("%"),
- LTLT("<<"),
- GTGT(">>"),
- GTGTGT(">>>"),
- PLUSEQ("+="),
- SUBEQ("-="),
- STAREQ("*="),
- SLASHEQ("/="),
- AMPEQ("&="),
- BAREQ("|="),
- CARETEQ("^="),
- PERCENTEQ("%="),
- LTLTEQ("<<="),
- GTGTEQ(">>="),
- GTGTGTEQ(">>>="),
- MONKEYS_AT("@"),
- CUSTOM;
- ...
- }
调用nextToken生成的字符集合都是一个Name对象,所有的Name对象都存储在Name.Table这个内部类中,可以参考另外一篇文章:
Keyworks会将在Token中所有的元素按照它们的Token.name先转化成Name对象,然后建立Name和Token的对应关系,这个关系保存在Keyworks类的key数组中。
Keywords类定义了如下重要的属性:
- /** The names of all tokens.
- */
- private Name[] tokenName = new Name[values().length];
初始化时填充tokenName,代码如下:
- private void enterKeyword(String s, Token token) {
- Name n = names.fromString(s);
- tokenName[token.ordinal()] = n;
- if (n.getIndex() > maxKey) {
- maxKey = n.getIndex();
- }
- }
则数组的值为:
...
因为有tokenName的枚举常量其ordinal从3开始,到109结束。
然后就可以借助tokenName来完成name到Token的映射了,涉及到的属性如下:
- /**
- * Keyword array. Maps name indices to Token.
- */
- private final Token[] key;
- /** The number of the last entered keyword.
- */
- private int maxKey = 0;
填充key的属性代码如下:
- protected Keywords(Context context) {
- // ...
- key = new Token[maxKey+1];
- for (int i = 0; i <= maxKey; i++) {
- key[i] = IDENTIFIER;
- }
- for (Token t : values()) {
- if (t.name != null) {
- int oi = t.ordinal();
- int ti = tokenName[oi].getIndex();
- key[ti] = t;
- }
- }
- }
maxKey值为2905。key中的下标为Name的index值,而值就是Token。其值如下:
- 2630=abstract
- 2638=assert
- 1195=boolean
- 2644=break
- 1054=byte
- 2649=case
- 2653=catch
- 1092=char
- 63=class
- 2658=const
- 2663=continue
- 56=default
- 2671=do
- 1173=double
- 2673=else
- 2677=enum
- 2681=extends
- 2688=final
- 2693=finally
- 1153=float
- 2700=for
- 2703=goto
- 2707=if
- 2709=implements
- 2719=import
- 2725=instanceof
- 1115=int
- 2735=interface
- 1135=long
- 2744=native
- 2750=new
- 2753=package
- 2760=private
- 2767=protected
- 2776=public
- 2782=return
- 1072=short
- 2788=static
- 2794=strictfp
- 51=super
- 2802=switch
- 2808=synchronized
- 47=this
- 2820=throw
- 2825=throws
- 2831=transient
- 2840=try
- 1219=void
- 2843=volatile
- 2851=while
- 2856=true
- 2860=false
- 2865=null
- 2869=(
- 2870=)
- 2871={
- 2872=}
- 2873=[
- 2874=]
- 45=;
- 44=,
- 43=.
- 2875=...
- 2878==
- 2598=>
- 2597=<
- 2574=!
- 2569=~
- 2879=?
- 2880=:
- 2603===
- 2599=<=
- 2601=>=
- 2605=!=
- 2607=&&
- 2609=||
- 2570=++
- 2572=--
- 2568=+
- 1=-
- 46=*
- 0=/
- 2587=&
- 2588=|
- 2589=^
- 2586=%
- 2590=<<
- 2592=>>
- 2594=>>>
- 2881=+=
- 2883=-=
- 2885=*=
- 3=/=
- 2887=&=
- 2889=|=
- 2891=^=
- 2893=%=
- 2895=<<=
- 2898=>>=
- 2901=>>>=
- 2905=@
Scanner类中的nextToken()方法解读的更多相关文章
- Java Scanner类中next()和nextLine()方法的区别
今天在练习中遇到了调用Scanner类中的nextLine()输入字符串自动跳过的问题,在博客上看了两篇解答,原来是nextLine()误认了前面next()输入时的Enter,但还是想了一会儿才弄清 ...
- Java基础之Scanner类中next()与nextLine()方法的区别
java中使用Scanner类实现数据输入十分简单方便,Scanner类中next()与nextLine()都可以实现字符串String的获取,所以我们会纠结二者之间的区别. 其实next()与nex ...
- 12-01 Java Scanner类,Scanner类中的nextLine()产生的换行符问题
分析理解:Scanner sc = new Scanner(System.in); package cn.itcast_01; /* * Scanner:用于接收键盘录入数据. * * 前面的时候: ...
- Java中是否可以调用一个类中的main方法?
前几天面试的时候,被问到在Java中是否可以调用一个类中的main方法?回来测试了下,答案是可以!代码如下: main1中调用main2的主方法 package org.fiu.test; impor ...
- 重写Object类中的equals方法
Object是所有类的父亲,这个类有很多方法,我们都可以直接调用,但有些方法并不适合,例如下面的student类 public class Student { //姓名.学号.年纪 private S ...
- PHP通过反射方法调用执行类中的私有方法
PHP 5 具有完整的反射 API,添加了对类.接口.函数.方法和扩展进行反向工程的能力. 下面我们演示一下如何通过反射,来调用执行一个类中的私有方法: <?php //MyClass这个类中包 ...
- Java String类中的intern()方法
今天在看一本书的时候注意到一个String的intern()方法,平常没用过,只是见过这个方法,也没去仔细看过这个方法.所以今天看了一下.个人觉得给String类中加入这个方法可能是为了提升一点点性能 ...
- Java线程状态及Thread类中的主要方法
要想实现多线程,就必须在主线程中创建新的线程对象. 不论什么线程一般具有5种状态,即创建,就绪,执行,堵塞,终止. 创建状态: 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时 ...
- [SignalR]在非Hub继承类中使用脚本方法
原文:[SignalR]在非Hub继承类中使用脚本方法 新建一个普通类OutHub,里面包含一个脚本方法OutHubTest. 因为大家知道,若能让脚本调用到的话,必须继承Hub,那怎么实现了?通过G ...
随机推荐
- 自定义View--滚动View
实现这么一个效果,一个布局中有一个View,那个View会随着我们手指的拖动而滑动,这种效果该如何实现? 我们第一反应应该是自定义一个DragView类继承View,然后重写onTouchEven ...
- 20145233《网络对抗》Exp9 Web安全基础实践
20145233<网络对抗>Exp9 Web安全基础实践 实验问题思考 SQL注入攻击原理,如何防御? SQL注入攻击就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符 ...
- Python学习-10.Python函数定义(二)
在Python中定义函数的时候,可以使用参数默认值的方式定义函数 例子: def welcome(who,state='is',action='talking'): print(who,state,a ...
- Java Web系列:JDBC 基础
ADO.NET在Java中的对应技术是JDBC,企业库DataAccessApplicationBlock模块在Java中的对应是spring-jdbc模块,EntityFramework在Java中 ...
- 修改TFS附件大小的限制
在TFS服务器使用浏览器上打开如下地址:http://localhost:8080/tfs/<CollectionName>/WorkItemTracking/v1.0/Configura ...
- 好看的table样式
收藏个好看的table样式 <style type="text/css">table.gridtable { font-family: verdana,arial,sa ...
- iOS 优秀开源项目
1. 界面 iCarousel: 用来做图片旋转显示效果(相册). MZTimerLabel:用来制作一个计时并显示时间的 label. MSDynamicsDrawerViewController, ...
- c语言第六次作业---结构体&文件
1.本章学习总结 1.1思维导图 1.2学习体会 这次应该是本学期最后一次博客了,总结一下这个学期的学习,一开始就基础薄弱还一直畏难一直懒惰,不想去解决问题导致后面问题越来越多就觉得学习越来越难,后面 ...
- Hibernate 框架学习
什么是hibernate框架? 1.它是应用在javaee 三层架构中的dao层 它的底层就是JDBC 它对JDBC进行了封装,好处就是不用写jdbc的代码,和sql语句,它是一个开源的轻量级框架,现 ...
- [CSS3] 边栏导航动画
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...