Java Scanner 类精讲:控制台交互

1.1 什么是 Scanner?

Scanner 是 Java 5.0 新增的基于字符流的解析工具类 ,位于java.util包下。它的核心设计目标是简化用户交互和数据读取场景的开发工作:

  • 把底层的字节流(InputStream) ,自动转换为字符流,支持多种字符集编码;
  • 基于自定义分隔符(默认空格、换行符) ,将输入流切分成独立的令牌;
  • 将字符串类型的令牌,自动转换成开发者需要的基本数据类型(int、long、double、boolean 等)和字符串类型,内置正则校验逻辑。

1.2 Scanner 的核心应用场景

Scanner 不是用来处理高性能 IO 的工具类,它的核心适配场景,集中在人机交互和简单数据读取,这也是我们今天重点覆盖的范围:

场景分类 具体用途 工业级使用限制
控制台用户交互 读取用户输入的的用户名、密码、年龄、订单号、确认指令(y/n) 不推荐在高并发场景的后端服务中使用,仅用于控制台程序或内部工具的人机交互;
简单文件读取 读取配置文件、小型日志文件、批量导入的文本数据(如 csv、txt) 超过 100MB 的大文件,必须搭配缓冲区优化,或改用BufferedReader;
网络请求流解析 读取简单的 HTTP 响应、Socket 输入流,按分隔符拆分数据 复杂网络协议或高并发场景,必须改用 Netty 等 NIO 框架;
字符串分词处理 拆分复杂格式的字符串(如逗号分隔的 ID 列表、JSON 片段提取) 复杂结构化数据,推荐使用 Jackson、Gson 等专业序列化工具;

1.3 Scanner vs BufferedReader:核心差异

很多开发者会混淆这两个读取工具类,二者在设计目标、底层逻辑、性能表现上完全不同,选型错误是线上故障的根源之一:

对比维度 Scanner BufferedReader
核心设计目标 简化数据解析:将字节流转换为字符流,自动拆分令牌、转换数据类型 提升 IO 读取性能:为底层字符流提供 8KB 的缓冲能力,减少 IO 交互次数;
处理流类型 字节流(InputStream)、字符流(Readable) 字符流(Reader),可通过InputStreamReader适配字节流;
数据读取方式 基于自定义分隔符,将流拆分为令牌,再进行类型转换 按字符数组、行、固定长度读取流,无内置令牌拆分逻辑;
数据类型转换 内置支持基本数据类型、字符串转换,内置正则校验 仅支持读取字符或字符串,需要手动进行类型转换;
读取效率 慢:final 类,无锁设计,内置解析逻辑,单次读取涉及多次 IO 操作 快:abstract 类,采用装饰器模式,单次读取涉及少量 IO 操作;
线程安全性 非线程安全,多线程场景下必须做同步处理 非线程安全,多线程场景下必须做同步处理;
工业级选型场景 控制台交互、简单文件解析、低并发场景的数据流处理 大文件读取、高并发场景的数据流处理、高性能 IO 场景;

【博主经验批注】:我在生产环境见过用 Scanner 读取 10GB 级别的日志文件,导致整个服务的 CPU 占用率飙升至 100%,最终触发 OOM。核心原因就是 Scanner 的设计目标是简化解析,而不是高性能读取。

2.1 基本使用步骤

Scanner 的工作逻辑非常固定,所有场景下的编码步骤都是标准的 4 步模板:

复制代码
// 1. 引入Scanner类,必须在文件顶部导入

import java.util.Scanner;

public class ConsoleInteractionDemo {

    public static void main(String\[] args) {

        // 2. 构造Scanner对象,传入底层字节流,这里用System.in读取控制台输入

        Scanner scanner = new Scanner(System.in);

        

        // 3. 调用nextXxx()方法,读取并解析用户输入,转换成目标类型

        System.out.print("请输入您的用户名:");

        String username = scanner.nextLine(); // 读取一行字符串,包含空格,直到遇到换行符

        

        System.out.print("请输入您的年龄:");

        int age = scanner.nextInt(); // 读取一个整数,自动跳过分隔符,做数字格式校验

        

        System.out.print("请输入您的身高(单位:米):");

        double height = scanner.nextDouble(); // 读取一个浮点数,自动做格式校验

        

        System.out.print("请输入您的确认指令(y/n):");

        String confirm = scanner.next(); // 读取一个令牌,默认以空格、换行符为分隔符

        

        // 4. 关闭Scanner流,释放底层IO资源,避免资源泄漏

        scanner.close();

    }

}

2.2 核心 API 用法差异解析

这是初学者最容易搞混的点,不同的读取方法,对分隔符的处理逻辑完全不同------ 理解这一点,是后续规避经典坑的前提。

方法名 作用描述 分隔符处理逻辑 返回值 适配场景
nextLine() 读取一整行输入 ,包含空格,直到遇到换行符 会读取当前位置到换行符之间的所有字符,会吃掉换行符本身,将光标移动到换行符之后的下一个位置 字符串,不含末尾的换行符 读取包含空格的用户名、地址、多行备注信息;
next() 读取下一个有效令牌,默认以空格、换行符、制表符为分隔符 会自动跳过开头的所有分隔符,然后读取直到下一个分隔符之前的所有字符,不会吃掉末尾的分隔符,光标停在分隔符之前 字符串,不含首尾的分隔符 读取不包含空格的指令、订单号、手机号、验证码;
nextInt()/nextLong()/nextDouble() 读取并解析下一个令牌,转换成对应的基本数据类型 内部调用next()读取字符串令牌,再尝试转换为目标类型,不会吃掉末尾的分隔符,光标停在分隔符之前 对应的基本数据类型 读取年龄、金额、数量、分页参数等数字类输入;
nextBoolean() 读取并解析下一个令牌,转换为布尔类型 内部调用next()读取字符串令牌,再校验是否为true/false(忽略大小写) 布尔类型 读取确认状态、开关标记、布尔类指令;
hasNext()/hasNextXxx() 校验输入流中是否还有下一个令牌,是否匹配指定的正则表达式 会阻塞等待用户输入,直到检测到分隔符或流结束标记,不会移动光标 布尔类型,有下一个有效令牌则返回true 循环读取输入、做输入合法性校验,避免异常;

2.3 正确关闭流资源

Scanner 实现了Closeable接口,必须调用close()方法关闭流,释放底层 IO 资源,否则会导致资源泄漏,长期运行会导致文件句柄或 socket 连接耗尽。

工业级场景下,必须使用try-with-resources 语法,自动关闭流资源,保证异常发生时也能正常释放资源,不用手动调用close()方法。代码示例如下:

复制代码
public class TryWithResourcesDemo {

    public static void main(String\[] args) {

        // try-with-resources语法:在try关键字后的括号中声明Scanner对象,try代码块执行完后会自动关闭

        try (Scanner scanner = new Scanner(System.in)) {

            System.out.print("请输入您的用户名:");

            String username = scanner.nextLine();

            System.out.println("您输入的用户名是:" + username);

        } // 无论是否发生异常,这里都会自动调用scanner.close()方法,释放IO资源

    }

}
相关推荐
一晌小贪欢14 分钟前
Python基础:第 09 篇 · while 循环:重复直到不满足
开发语言·c++·python·数据可视化·面向对象·python办公
xiaoye-duck1 小时前
《C++ 项目:手写大模型接入 SDK》LLMManager架构设计与实现
开发语言·网络·c++·语言模型
一条大祥脚1 小时前
CuTe 与 CuTe DSL:从 C++ 模板元编程到 Python DSL 的 GPU 编程演进
开发语言·c++·python·cutlass·cute·cute dsl
学长毕设2 小时前
基于SpringBoot的野生动物公益保护平台设计与实现-附源码
java·spring boot·后端
FRing032 小时前
vue2干货总结8(路由模块封装、router-link、声明式导航、路由重定向、编程式导航)
开发语言·前端·vue.js·vue2
旖旎夜光2 小时前
【C++ 基础入门】C++ 多态(上):虚函数、重写、override/final 与抽象类
开发语言·c++·笔记·学习
SL_staff2 小时前
离散制造排程破局:JVS-APS 四步闭环落地实践(开发者视角)
java·产品·全栈
这个昵称不容易2 小时前
SpringBoot 美容院管理系统:232个Java文件315个接口,5套角色从预约到发货一整条链路
java·springboot·毕设
starzy19902 小时前
深入理解Scala嵌套函数:作用域封装之道
开发语言·scala
Sakura_j_h_2 小时前
从零写一个 MCP stdio Server:用 Python 校验博客文章格式
开发语言·python·自动化·ai工具·mcp·json-rpc