FEATURED · 精选文章

Java Scanner从入门到进阶:彻底搞懂nextLine与nextInt的坑和读取机制

发布时间 / 2026/9/10 8:11:30
来源 / 创域科博编辑部
栏目 / 资讯中心
Java Scanner从入门到进阶:彻底搞懂nextLine与nextInt的坑和读取机制 很多人学Java第一课就是键盘输入用的就是Scanner。但说实话Scanner这个类看起来挺简单实际深入下去坑不少。我见过不少工作两三年的开发一碰到nextLine和nextInt混用就出问题还有人在循环读取时被hasNext阻塞搞得一头雾水。这篇文章就把Scanner从入门到实战的完整路径梳理一遍包括那些面试官爱问的细节以及实际开发中为什么有人会弃用它。1. 从System.in到Scanner输入读取的本质是什么1.1 System.in到底是什么很多初学者搞不明白Scanner和System.in的关系。直接说结论Scanner是一个“解析器”System.in才是真正的输入来源。System.in是Java标准库里的一个InputStream对象它代表“标准输入流”默认情况下就是键盘。但System.in本身非常原始——它只能以字节为单位读取数据你按下一个字符它给你一个字节的二进制数据。要是想从键盘读入一个整数“100”System.in会给你三个字节1、0、0对应的ASCII码然后你的程序得自己把这些字节拼起来、转成int。这活儿做一次两次还行每次输入都这么干代码就没法看了。Scanner的出现就是解决这个问题。它内部持有System.in或者其他任何InputStream、Readable然后把你从键盘敲进去的一整行字符按空格、Tab、换行等分隔符拆分成一个个“token”再根据你的需要转换成int、double、String等类型。import java.util.Scanner; public class Demo { public static void main(String[] args) { Scanner sc new Scanner(System.in); System.out.print(请输入姓名); String name sc.nextLine(); System.out.print(请输入年龄); int age sc.nextInt(); System.out.println(姓名 name 年龄 age); sc.close(); } }这段代码大概是所有Java初学者都写过的。从使用角度来说三步创建Scanner对象、调用nextXxx()方法读取数据、用完关闭。但如果你只是停留在这一步后面那些问题迟早会找上你。1.2 Scanner的三种构造方式与底层分隔符机制Scanner有好多构造函数但实际开发中常用的就三种new Scanner(System.in)从标准输入读取也就是键盘这是教学和简单工具类里最常见的new Scanner(File file)从文件读取注意这里要处理FileNotFoundExceptionnew Scanner(String str)从字符串读取常用于解析一段固定格式的文本测试代码时特别好用。很多教程会忽略分隔符delimiter这件事但这恰恰是理解Scanner行为的关键。Scanner默认使用空白字符作为分隔符包括空格、Tab、换行符。当你调用nextInt()时它会跳过所有的分隔符找到下一个token并尝试解析成int。有个细节很值得注意Scanner不会在读取完一个token后自动“消费”掉后面的分隔符。比如你输入“18\n”调用nextInt()读到18之后换行符还在缓冲区里躺着。这时候你要是直接调nextLine()它读到的就是一个空字符串因为nextLine()的语义是“读取到换行符为止”而换行符已经在缓冲区开头了。这个行为特征会在后面第4节展开讲但这里我建议你先记住一个结论Scanner的各个next方法对分隔符的处理策略不是统一的next系列是“跳过前导分隔符再读取token”nextLine是“直接读到行尾”这个差异百分之百会在实战里给你挖坑。1.3 为什么不直接用BufferedReader有人会问“既然Scanner包装了System.in那System.in外面能不能直接再套一个BufferedReader去读”可以。事实上早期Java版本里BufferedReaderInputStreamReader是读取键盘输入的标准做法而且代码写起来反而更啰嗦BufferedReader reader new BufferedReader(new InputStreamReader(System.in)); String line reader.readLine(); // 返回一行字符串区别在哪里呢BufferedReader的readLine()只返回字符串要int得自己Integer.parseInt()要double得Double.parseDouble()麻烦一点。但它的性能比Scanner好得多因为Scanner每个next方法都要做正则匹配来做分隔符切分这个后面会细说开销非常大。如果你的程序需要从System.in持续读取海量数据用Scanner是真的会明显变慢的。所以这俩不是谁替代谁的关系而是适用场景不同。我在做算法题时习惯用BufferedReader写小工具和交互式命令行程序时用Scanner因为后者代码简洁、异常处理清晰。2. Scanner读取数据全流程拆解从创建到关闭的生命周期2.1 Scanner对象创建时背后发生了什么new Scanner(System.in)这行代码看起来轻描淡写背后干了不少事。Scanner构造时会根据输入源的类型做适配。当传入的是InputStream时它内部会把它包装成InputStreamReader然后根据平台默认字符集去解码字节流。如果传的是File它也会用同样的方式只是不用走标准输入。对于File构造方式建议显式指定字符集尤其是文件里有中文的时候不指定的话在Windows上容易因为GBK和UTF-8不一致而乱码。还有一点值得知道Scanner不是线程安全的。如果你在多个线程里共享同一个Scanner去读输入会出现数据竞争、读到的内容错乱等问题。多线程环境下要么每个线程自己new一个Scanner要么用外部同步手段锁住读取操作。2.2 最常用的next/nextLine/nextInt家族方法Scanner提供了非常丰富的读数方法按用途分成几类读取单个tokennext()、nextInt()、nextDouble()、nextLong()、nextBoolean()等读取整行nextLine()判断是否有下一个hasNext()、hasNextInt()、hasNextDouble()等。next()和nextLine()的区别是初学者最容易搞混的地方。next()是读取直到遇到分隔符为止的token默认情况下换行符也算分隔符所以next()不会包含换行nextLine()是读取到换行符为止把这一整行都给你包括空格但不包括换行符本身。Scanner sc new Scanner(System.in); String word1 sc.next(); // 输入Hello Worldword1是Hello String word2 sc.next(); // word2是World String line sc.nextLine(); // 如果缓冲区还有换行符这里会返回空串经典坑这是很多作业题里“为什么我的程序跳过了输入”的根源。输入一次“Hello World”按了回车实际缓冲区里是“Hello World\n”。第一次next()读到Hello第二次next()读到World但第三次nextLine()遇到的是缓冲区里残留的\n直接返回空串。要说清楚的是这不算什么Bug而是Scanner的“按分隔符切token”和“按行读取”两种模式的设计差异。理解了这一点你就能预判它的行为。2.3 识别输入结束hasNext与hasNextXxx的正确打开方式在从键盘和从文件读取时判断“还有没有下一个输入”的机制完全不同。从文件读取时文件读完了hasNext()会返回false循环自然结束。但从键盘读取时hasNext()会阻塞等待你输入如果你不主动发送“输入结束”的信号它就一直在那等着。怎么发送输入结束信号呢在Windows的命令行窗口按CtrlZ然后回车在Linux/macOS的终端里按CtrlD。Scanner sc new Scanner(System.in); int sum 0; while (sc.hasNextInt()) { sum sc.nextInt(); } System.out.println(sum sum);这段代码在OJ在线判题系统上很常见。系统会通过输入重定向把一段文本喂给stdinhasNextInt()判断到文件尾后返回false循环退出。但你要是自己在控制台跑这个程序不按CtrlD/ CtrlZ它就会一直等你输数字看起来像“卡死”了一样。这里还要提一下hasNext()和hasNextXxx()的区别。hasNext()只判断“还有没有下一个token”不管它是什么类型hasNextInt()则先尝试解析下一个token是否为int是才返回true而且这个判断不会消耗输入内容。什么意思呢就是hasNextInt()检查后如果你接下来调用nextInt()得到的结果和hasNextInt()看到的是同一个token不会被跳过。这个特性在写防御性输入解析时非常有用。3. hasNext()方法详解从阻塞语义到循环读取实战3.1 hasNext()为什么会阻塞这个问题在很多线程讨论里出现过“为什么我的hasNext()不返回false我明明输入完了。”——这个问题的核心在于对“输入完”这件事的理解。对于文件输入流来说“输入完”意味着文件末尾EOFhasNext()感知到EOF后返回false是自然的事。但System.in是一个等待用户主动“喂数据”的流你把键盘敲完按下回车这只是一段文本进入了缓冲区系统并不知道你是不是还要继续输入。所以hasNext()在等待输入时处于阻塞状态实时等着新数据到来。这就像你跟一个人对话你问他“你还有话要说吗”他不回答你就只能等。他要不理你你一直等下去也没有意义。所以对于交互式程序用hasNext()做无限循环必须有一个明确的终止信号。3.2 从标准输入、文件、字符串三个来源验证hasNext行为为了彻底搞明白hasNext的工作原理我建议你做三组小实验。第一组从System.in读取程序运行后不输入任何内容直接用idea或命令行跑。你会发现程序卡在hasNext()上。这时你在控制台输入几个字符比如“abc”再按回车hasNext()返回true接着你继续等待它再次阻塞。直到你发送EOFCtrlD或CtrlZhasNext()才返回false循环结束。第二组从文件读取。写一个data.txt里面放几行数字用new Scanner(new File(data.txt))读取hasNextInt()在文件读完时返回false程序正常结束。第三组从String读取。new Scanner(1 2 3)配合while(hasNextInt())循环三个token读完hasNextInt()就返回false了不会阻塞也不会有意外。这组验证的价值在于让你直观建立“输入源决定阻塞行为”的认知。同一段循环逻辑只是换了输入源行为完全不同。很多人在在线评测系统里写得好好的代码一拿回本地跑就“卡死”不是代码逻辑变了而是输入源变了。3.3 用hasNext循环读取未知长度的输入实战中很常见的一个场景是事先不知道用户会输入多少个数字需要一直读到输入结束才能停止。Scanner sc new Scanner(System.in); ListInteger numbers new ArrayList(); while (sc.hasNextInt()) { numbers.add(sc.nextInt()); }这个模式在OJ和本地测试里都好用但有两点要注意如果用户输入了一个非数字内容比如“abc”hasNextInt()会返回false循环退出。但那个“abc”并没有被消费掉它还在缓冲区里下一个nextLine()会读到它。不要试图通过hasNextInt()的false来判断“用户是否输入了合法数字”的校验逻辑。因为如果你需要循环提示用户重新输入每次失败后都应该把非法的token消费掉用next()把当前token吞掉否则容易死循环。举个例子你写一个交互式程序“请输入一个整数”用户输了个“abc”你想提示后让他重新输。如果只写while(!sc.hasNextInt()) { System.out.println(请输入整数); }那当hasNextInt()返回false时你循环体里什么也没做再去判断hasNextInt()时依然是对同一个“abc”做判断永远返回false就死循环了。正确做法是每次判断失败后执行一次sc.next()把“abc”清掉。4. nextLine系列与token读取的相爱相杀换行符陷阱的根因4.1 经典坑nextInt后直接nextLine读不到内容这段代码几乎是Java初学者必遇的坑Scanner sc new Scanner(System.in); System.out.print(请输入年龄); int age sc.nextInt(); System.out.print(请输入姓名); String name sc.nextLine(); System.out.println(age age , name [ name ]);输入“20\n张三\n”之后程序输出会是age 20, name []。问题根源前面已经提到了nextInt()读到20后后面的换行符还在缓冲区。nextLine()从缓冲区读数据遇到的第一个字符就是换行符于是直接把一行结束了返回空串。后面的“张三”根本没机会被读到。解决的思路有几个在nextInt()之后多调一次nextLine()把残留的换行符消费掉全程使用nextLine()读取再手动Integer.parseInt()转换使用next()读取姓名但要考虑姓名含空格的情况。第二种方式在工程上更推荐因为逻辑统一不会混用两套读取策略。比如Scanner sc new Scanner(System.in); System.out.print(请输入年龄); int age Integer.parseInt(sc.nextLine()); System.out.print(请输入姓名); String name sc.nextLine();这样虽然多一次字符串到整数的转换但规避了nextLine和nextXxx混用的所有坑代码可读性还更好。在需要读多行、多类型数据的场景下统一采用“读一行字符串再自己解析”的方式是最稳的。4.2 为什么会残留换行符Scanner的分隔符匹配机制要彻底理解这个坑得回到Scanner内部的工作机制。Scanner内部维护了一个缓冲区并有“当前匹配到的分隔符”和“上次读取token后的位置”等状态。以nextInt()为例它的大致过程是先跳过所有满足当前分隔符模式的字符默认是空白符包括空格、Tab、换行从第一个非分隔符开始匹配一个连续的token连续的非分隔符字符尝试把token解析成int返回int但注意步骤1里跳过的分隔符已经被消费了步骤2之后、token之后紧接着的分隔符并没有被读取。关键就在第4步。输入“20\n”时nextInt()消费了“20”“\n”留在缓冲区。下一次nextLine()被调用时它的语义是“从当前位置开始一直读到下一个换行符为止”而当前位置就是“\n”的开头所以它立刻返回空字符串。理解这个机制后你就明白为什么nextLine和nextXxx这么“不兼容”了它们对分隔符的处理策略不同。nextXxx族方法都是“findWithinHorizon”式的先跳过前导分隔符再匹配tokennextLine是“从当前位置到行尾”式的。要避免踩坑要么尽量统一用同一种读取模式要么在每个nextXxx后显式消费掉残留换行。4.3 数字读取会跳行、循环读取行数异常等连锁问题换行符残留导致的Bug不止“直接读不到内容”一种还会引起更隐蔽的连锁问题。比如你写一个循环读取多个学生信息的程序Scanner sc new Scanner(System.in); System.out.print(请输入学生人数); int n sc.nextInt(); for (int i 0; i n; i) { System.out.print(请输入姓名); String name sc.nextLine(); // 第一次循环会读到残留换行符 System.out.print(请输入分数); double score sc.nextDouble(); }第一次循环时nextLine()吃到的是上一行回车留下的换行name变成空串到了第二次循环nextLine()才正常读到名字但这时候用户输入的节奏和数据顺序可能全乱了。这种Bug尤其烦人因为它不是每次都出错而是错位数据错位比数据丢失更难排查。遇到这类问题我的排查建议是先在程序开头把“读入”和“解析”分开把所有输入都先用nextLine()接收成字符串在内存里再按需解析实在没法统一再考虑每次nextXxx后补一次nextLine()处理。5. Scanner的异常与防御性编程把崩溃拦在输入之前5.1 InputMismatchException和NoSuchElementException区别与触发条件Scanner使用过程中最容易遇到两个异常。InputMismatchException当调用nextInt()但下一个token无法解析成int时抛出来。比如用户输入了“abc”你调用nextInt()就会抛这个异常。注意发生这个异常时那个非法的token并不会被消费掉它还留在缓冲区里如果后续代码再次调用nextInt()异常会继续发生需要手动消费掉非法token。NoSuchElementException当Scanner已经没有输入可用时你还去调用nextXxx()。比如文件读取已经到末尾你再执行nextLine()就会抛这个异常。它的触发条件与hasNextXxx()返回false是一致的。所以在调用nextXxx()之前先判断hasNextXxx()是防御这类异常的常见方式。这两个异常都比较直白但很多人在捕获时容易犯一个错误在catch块里继续调用scanner方法做“恢复”结果因为token没被消费又抛出同一个异常形成难以理解的错误链。正确做法是在catch块里先调用next()把非法token清掉再重新提示用户输入。Scanner sc new Scanner(System.in); int number; while (true) { System.out.print(请输入一个整数); if (sc.hasNextInt()) { number sc.nextInt(); break; } else { System.out.println(输入格式不正确请重试); sc.next(); // 关键消费掉非法的token } }5.2 FileNotFoundException和IllegalStateException容易被忽略的两个异常从文件读取时FileNotFoundException是一个受检异常编译器强制你处理。这个好理解文件不存在或者路径不对就会抛。但有个细节容易被忽略在Windows下路径分隔符要写双反斜杠或者用正斜杠不然路径解析容易出问题。IllegalStateException则与Scanner的生命周期有关。当你对Scanner调用close()之后如果继续调用next或hasNext方法就会抛IllegalStateException提示“Scanner closed”。这就是为什么不要在循环中间随手关Scanner否则下次循环进来就异常了。还有一点容易被忽略的是Scanner在close()时会关闭它底层的输入流。如果底层是System.in关闭后你整个程序的后续输入都会失效。所以在工具类里如果你把System.in包装在了Scanner里又在某个分支里提前close了后面的代码再想用System.in就会失败。5.3 配合hasNextXxx做校验推荐输入范式写出健壮交互式输入代码的核心是“先验证再读取不合法就清token”。一个通用的范式可以总结成这几条总是先用hasNextXxx()判断判断失败时先用next()把非法token消费掉再提示重新输入判断成功时再用nextXxx()真正读取循环次数不固定时用while而不是if避免只校验一次就退出尽量把输入读取的逻辑从业务逻辑里抽离出来。举个例子一个同时支持多个数字累加和退出命令的工具Scanner sc new Scanner(System.in); double sum 0; System.out.println(请输入数字输入end结束); while (sc.hasNext()) { if (sc.hasNextDouble()) { sum sc.nextDouble(); } else { String token sc.next(); if (end.equalsIgnoreCase(token)) { break; } else { System.out.println(无法识别的输入: token); } } } System.out.println(sum sum);这段代码的主要优势在于不管用户输入数字、字母、单词都不会抛异常程序可以正常走完遇到end才退出逻辑清晰。这种“永不崩溃”的交互式输入风格在实际工具类开发中非常有价值。6. 不止是键盘Scanner读取文件与字符串流的高级玩法6.1 用Scanner按行读取文件读取CSV与配置文件Scanner从文件读取的用法日常开发中可以用来快速处理一些格式规整的小文件尤其是行结构明显的CSV或配置文件。try (Scanner sc new Scanner(new File(data.csv), UTF-8)) { while (sc.hasNextLine()) { String line sc.nextLine(); String[] fields line.split(,); // 处理每行字段 } } catch (FileNotFoundException e) { e.printStackTrace(); }这里有两个工程细节第一构造Scanner时建议显式传字符集“UTF-8”避免在某些平台上默认字符集不一致导致中文乱码第二try-with-resources可以自动关闭Scanner不用手动写close。Scanner读取CSV的短板也是要清楚的它对引号包裹的字段比如“张,三”这样的字段名不支持因为默认分隔符是空白或逗号不会考虑引号内的逗号。如果你的CSV是这种带引号转义的格式建议老老实实用开源的CSV库比如OpenCSV或Apache Commons CSV不要自己用Scanner硬撑。Scanner更适合快速读取格式简单的文本而不是做严肃的文件解析。6.2 从字符串构造Scanner解析结构化文本第二种快速解析文本的方式是从String构造Scanner。这个方法在写单元测试、解析命令行参数、切分简单配置时特别有用。String data id1,name张三,score90\nid2,name李四,score85; Scanner sc new Scanner(data); while (sc.hasNextLine()) { String line sc.nextLine(); String[] parts line.split(,); for (String part : parts) { String[] kv part.split(); System.out.println(kv[0] - kv[1]); } }这里再补一个非默认分隔符的使用方式。Scanner的useDelimiter方法可以自定义分词规则比如我想直接用“,”和换行符做分词就可以写成Scanner sc new Scanner(data); sc.useDelimiter([,\\n]); while (sc.hasNext()) { String token sc.next(); System.out.println(token); }这个方式在解析简单格式时非常简洁但注意一旦用了自定义分隔符默认的空格分隔就没有了可能跟你预想的不一致。所以自定义分隔符前先想清楚你的文本包含哪些字符。6.3 useDelimiter自定义分隔符一个正则的细节useDelimiter接收的是一个正则表达式字符串默认值其实是“\p{javaWhitespace}”一个或多个空白符。这个默认值在英文文档里被称为“whitespace”。你可以把它替换成任意正则比如“,”、 “;|\n” 、 “\s*,\s*”等。有个细节特别值得注意正则的匹配是以“连续匹配”为原则的。如果你设置useDelimiter(,)而文本里同时出现了“”中文全角逗号和“,”英文半角逗号你期望Scanner只按英文逗号切分但中文逗号会被当成普通token的一部分保留下来。这在解析用户输入时容易踩坑所以经常有人问“为什么我的分词不干净”排查方向通常就在分隔符的中英文字符上。7. 大厂面试高频追问Scanner相关的底层原理与实战应答7.1 为什么说Scanner性能不如BufferedReader面试官问起Scanner多半会追问一个点“它性能怎么样”Scanner性能瓶颈主要出在内部的正则匹配上。nextXxx()方法在语法上使用的是Java正则表达式来匹配分隔符每次调用都需要编译和匹配正则这个过程比BufferedReader的简单字符判断要昂贵得多。当需要读入上百万行数据时两者性能差异会非常明显。在OJ或在线编程平台上高频的输入规模下Scanner慢是真实存在的。我的建议是如果是万元级以上的数据输入就直接用BufferedReader如果只是几个值、几十行配置Scanner的便利性更高没必要为了性能牺牲可读性。7.2 综合场景面试题如何从标准输入读取“不确定个数”的整数一道很常见的面试题是“从标准输入读取多个整数计算它们的和但数量未知。”如果面试官没有特别限制性能可以用Scanner的hasNextInt()循环。但如果你答完之后他追问一句“如果输入量特别大呢”你就要能切换到BufferedReader方案BufferedReader reader new BufferedReader(new InputStreamReader(System.in)); long sum 0; String line; while ((line reader.readLine()) ! null !line.isEmpty()) { for (String part : line.trim().split(\\s)) { sum Long.parseLong(part); } } System.out.println(sum);这个方案展示出你对BufferedReader的熟悉同时也体现了你明白hasNextInt的简单实现和性能瓶颈之间的取舍。两个方案都答得上面试官对你的印象会好不少。7.3 代码评审中Scanner资源管理的几个检查点在实际代码评审里涉及Scanner的代码有几个固定的检查点Scanner是否被close了如果是局部使用优先用try-with-resources是否把System.in包在Scanner里并关闭了关闭System.in会导致整个进程后续无法再读取标准输入所以像new Scanner(System.in)这种对象在命令行工具里通常不主动关闭是否在循环里重复创建ScannerScanner创建本身有一定开销循环里new Scanner是不良风格一般提前创建好nextLine和nextXxx是否混用混用时有没有处理残留换行符这些检查点不复杂但很能反映出一个人是不是真正在工程环境里写过输入相关的代码。我在评审时看到不少人把Scanner的close写进finally块遇到System.in也无脑关闭这个习惯会在大规模并发工具里引起诡异问题建议改掉。8. 真实项目里的Scanner使用经验与替代方案8.1 什么时候用Scanner简单交互工具和算法入门Scanner最适合的场景是程序需要和用户进行少量、交互式的文本输入。比如我写过一个内部命令行小工具让运维输入目标服务器IP、端口、超时时间。这种情况下Scanner简直完美代码短、异常提示清晰直接nextLine或nextInt方便得很。这类场景有几个共同特点一是输入量很小没有性能问题二是需要给用户明确提示交互感强三是输入格式受控用户不会故意输入一堆乱七八糟的字符。在这些场景下Scanner是比BufferedReader更好的选择因为代码简短也能兼顾可读性。8.2 什么时候放弃Scanner高并发IO、大数据量、特殊格式解析反过来这些场景会劝退Scanner第一大数据量文本处理。比如几十万行的日志文件、大规模数据集Scanner的正则匹配开销会迅速放大整个程序的吞吐量被拖慢。这种场景用BufferedReader或更专业的解析库会更靠谱。第二特殊格式解析。Scanner的分隔符模型是“按正则切token”本质上是无状态的。遇到JSON、XML、CSV中带引号转义的复杂格式Scanner就无能为力了。JSON用Jackson或GsonCSV用OpenCSVXML用DOM或SAX这些是业界标准做法。第三高并发多线程输入。Scanner不是线程安全的多个线程共享一个Scanner去读文件或流时数据会乱必须同步。但同步会让输入变成串行性能不升反降。正确的设计是每个线程单独持有一个输入流和解析器。第四需要随机访问或定位的输入场景。Scanner是流式的、只能顺序读取。如果你需要跳过前面若干行再读取或者反复回溯读取某段内容Scanner做不到通常用RandomAccessFile或者一次性读入内存处理。8.3 从JDK版本演进的视角看Scanner的定位最后想说一个趋势上的观察。JDK的输入API一直有演进从早期的InputStream、BufferedReader到后来的Scanner、Console再到Java 8的Files.lines、Stream API。新一代的API越来越强调“把数据看成流用声明式方式处理”。比如用Files.lines读取文件配合Stream的map、filter、collect整个数据处理的表达力比手动whilenextLine要强得多try (StreamString lines Files.lines(Paths.get(data.txt))) { long sum lines.mapToLong(Long::parseLong).sum(); System.out.println(sum); } catch (IOException e) { e.printStackTrace(); }这种写法简化了循环、关闭、异常处理等模板代码更适合函数式编程风格。不过这不是说Scanner就要被淘汰了。在需要交互输入、严格按token类型解析、或者代码极简的场景下Scanner依然有不可替代的价值。我的个人习惯是快速原型、算法练习、命令行交互工具用Scanner正式的数据处理模块用BufferedReader或专门的解析库涉及大文件的实时流处理会用Files.lines或Apache Commons IO这类更高层的封装。工具选型看场景不跟风也不厚此薄彼是写代码的基本原则。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻