0

0

Java do-while 循环异常行为解析与输入处理最佳实践

霞舞

霞舞

发布时间:2025-11-03 11:35:49

|

643人浏览过

|

来源于php中文网

原创

Java do-while 循环异常行为解析与输入处理最佳实践

本文深入探讨了java中`do-while`循环在处理用户输入时,因`system.in.read()`方法对输入缓冲区的特殊处理而导致的意外多次执行问题。通过分析回车换行符的影响,文章提出并演示了使用`java.util.scanner`类进行输入处理的解决方案,并提供了清晰的示例代码和最佳实践,旨在帮助开发者避免类似陷阱,编写更健壮的用户交互程序。

在Java编程中,当我们需要从控制台获取用户输入并进行循环验证时,do-while循环是一个常用的结构。然而,如果不正确地处理输入流,可能会遇到循环意外执行多次的问题,尤其是在使用System.in.read()方法时。本教程将深入分析这一现象的根本原因,并提供一个健壮的解决方案。

问题现象:do-while 循环的“多余”执行

考虑以下Java代码片段,它尝试构建一个简单的菜单选择系统:

public class Menu {

    public static void main(String[] args)
    throws java.io.IOException {
        char choice;
        do {
            System.out.println("Help on:");
            System.out.println(" 1. if");
            System.out.println(" 2. while");
            System.out.println(" 3. do-while");
            System.out.println(" 4. for");
            System.out.println(" 5. switch");
            choice = (char) System.in.read(); // 读取用户输入
        } while(choice < '1' || choice > '5'); // 循环条件:当输入不在 '1' 到 '5' 之间时继续循环
    }
}

当用户输入一个无效字符(例如 '6')并按下回车键时,我们期望程序在打印一次菜单后,如果输入无效,则再次打印菜单并等待新的输入。然而,实际运行结果可能如下所示,菜单内容被打印了三次,而不是一次:

Help on:
 1. if
 2. while
 3. do-while
 4. for
 5. switch
6
Help on:
 1. if
 2. while
 3. do-while
 4. for
 5. switch
Help on:
 1. if
 2. while
 3. do-while
 4. for
 5. switch
Help on:
 1. if
 2. while
 3. do-while
 4. for
 5. switch

这种现象表明,在用户输入 '6' 之后,循环体又“额外”执行了两次。

立即学习Java免费学习笔记(深入)”;

根本原因:System.in.read() 与输入缓冲区

造成上述问题的原因在于System.in.read()方法的行为以及操作系统的输入缓冲区。

Dora
Dora

创建令人惊叹的3D动画网站,无需编写一行代码。

下载
  1. System.in.read() 的特性:System.in.read()方法从标准输入流中读取一个字节,并将其作为int类型返回。当用户输入一个字符(如 '6')并按下回车键时,实际上发送给程序的是三个字节:

    • 用户输入的字符本身(例如 '6' 的ASCII码)。
    • 回车符(Carriage Return, \r,ASCII码 13)。
    • 换行符(Line Feed, \n,ASCII码 10)。 这三个字节会依次进入System.in的输入缓冲区。
  2. 循环中的处理:

    • 第一次循环: choice = (char) System.in.read(); 读取了用户输入的 '6'。此时,\r 和 \n 仍然留在输入缓冲区中。
    • 条件判断: choice '5' (即 '6' '5') 为真,因为 '6' > '5'。因此,循环继续。
    • 第二次循环: 再次执行choice = (char) System.in.read();。这次它读取了缓冲区中的 \r。
    • 条件判断: \r 的ASCII码是 13。13
    • 第三次循环: 再次执行choice = (char) System.in.read();。这次它读取了缓冲区中的 \n。
    • 条件判断: \n 的ASCII码是 10。10
    • 第四次循环: 再次执行choice = (char) System.in.read();。此时缓冲区已空,程序会阻塞,等待新的用户输入。

这就是为什么当用户输入一个字符后,do-while循环会“额外”执行两次,因为回车符和换行符也被System.in.read()当作有效的输入并参与了循环条件判断。

解决方案:使用 java.util.Scanner 类

为了避免System.in.read()带来的输入缓冲区问题,推荐使用java.util.Scanner类来处理用户输入。Scanner提供了更高级的方法,如nextInt()、nextLine()等,它们能够更好地解析和消耗输入流中的特定类型数据,并自动处理行终止符。

以下是使用Scanner改进后的代码:

import java.util.InputMismatchException; // 导入InputMismatchException用于处理非整数输入
import java.util.Scanner; // 导入Scanner类

public class MenuImproved {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in); // 创建Scanner对象
        int choice; // 使用int类型存储选择

        do {
            System.out.println("Help on:");
            System.out.println(" 1. if");
            System.out.println(" 2. while");
            System.out.println(" 3. do-while");
            System.out.println(" 4. for");
            System.out.println(" 5. switch");

            // 尝试读取整数输入
            try {
                choice = scan.nextInt();
            } catch (InputMismatchException e) {
                System.out.println("无效输入!请输入一个数字。");
                scan.next(); // 消耗掉错误的输入,防止无限循环
                choice = -1; // 设置一个无效值,确保循环继续
            }

        } while (choice < 1 || choice > 5); // 循环条件:当输入不在 1 到 5 之间时继续循环

        System.out.println("您选择了:" + choice); // 打印有效选择
        scan.close(); // 关闭Scanner对象,释放资源
    }
}

代码解析与优势

  1. Scanner scan = new Scanner(System.in);: 创建一个Scanner对象,它包装了System.in输入流。
  2. choice = scan.nextInt();: nextInt()方法会读取并解析输入流中的下一个整数。它会自动跳过任何空白字符(包括回车和换行符),直到找到一个整数。这意味着它会正确地只获取用户输入的数字,而不会将\r和\n作为后续输入。
  3. while (choice 5);: 循环条件保持不变,当用户输入的整数不在 1 到 5 之间时,循环会继续执行,再次提示用户输入。
  4. 异常处理 (try-catch): 增加了try-catch块来捕获InputMismatchException。如果用户输入的不是一个有效的整数(例如输入了字母),nextInt()会抛出此异常。
    • 在catch块中,我们打印错误信息。
    • scan.next();这一行非常重要,它会消耗掉输入流中导致InputMismatchException的非整数令牌。如果没有这一行,Scanner会不断尝试读取相同的无效输入,导致无限循环。
    • choice = -1; 将choice设置为一个明确的无效值,确保在发生异常时循环能够继续。
  5. scan.close();: 在程序结束前,关闭Scanner对象是一个良好的实践,以释放系统资源。

注意事项与最佳实践

  • 混合使用 nextInt() 和 nextLine(): 如果在读取整数(nextInt())、浮点数(nextDouble())或其他非行输入后,紧接着需要读取一整行文本(nextLine()),可能会遇到nextLine()直接读取到之前nextInt()留下的换行符而跳过用户输入的问题。解决方法是在nextInt()之后,手动调用一次scan.nextLine();来消耗掉剩余的换行符。
    int num = scan.nextInt();
    scan.nextLine(); // 消耗掉nextInt()留下的换行符
    String text = scan.nextLine();
  • 输入验证: 始终对用户输入进行验证,以确保其符合程序的预期。Scanner结合try-catch是实现这一目标有效方式。
  • 资源管理: 养成关闭Scanner对象的习惯,特别是在不再需要它时,以避免资源泄露。

总结

do-while循环在用户交互程序中非常有用,但System.in.read()方法在处理控制台输入时,由于其逐字节读取的特性以及对回车换行符的处理方式,容易导致意外的循环行为。通过理解输入缓冲区的机制,并采纳java.util.Scanner类及其提供的高级输入方法(如nextInt()),我们可以更健壮、更安全地处理用户输入,避免不必要的循环执行,从而编写出更可靠的Java应用程序。始终记住对输入进行验证和适当的异常处理,是构建高质量交互式程序的关键。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

106

2023.09.25

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1010

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

611

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

334

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

235

2025.08.29

常见的编码方式
常见的编码方式

常见的编码方式有ASCII编码、Unicode编码、UTF-8编码、UTF-16编码、GBK编码等。想了解更多编码方式相关内容,可以阅读本专题下面的文章。

647

2023.10.24

a和A对应的ASCII码数值
a和A对应的ASCII码数值

a的ascii码是65,a的ascii码是97;ascii码表中,一个字母的大小写数值相差32,一般知道大写字母的ascii码数值,其对应的小写字母的ascii码数值就算出来了,是大写字母的ascii码数值“+32”。想了解更多相关的内容,可阅读本专题下面的相关文章。

2249

2024.10.24

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

9

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

22

2026.03.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11.1万人学习

Java 教程
Java 教程

共578课时 | 80.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号