当前位置: 首页 > news >正文

第8章 第一阶段项目:命令行成绩统计器

第8章 第一阶段项目:命令行成绩统计器

第一阶段学了很多基础知识:

  • Java 程序运行。
  • 变量和类型。
  • 输入输出。
  • 条件判断。
  • 循环。
  • 方法。

如果这些知识只停留在小片段里,很容易学完就忘。项目的意义是把它们串起来。本章我们做一个命令行成绩统计器。

一、项目目标

程序运行后:

  1. 询问要输入多少个学生成绩。
  2. 逐个输入成绩。
  3. 每个成绩必须是 0 到 100 的整数。
  4. 输入非法时提示错误,并要求重新输入。
  5. 输入完成后统计:
    • 最高分。
    • 最低分。
    • 平均分。
    • 及格人数。
    • 及格率。
  6. 程序结构要拆成方法,不把所有逻辑堆在 main 里。

最终效果类似:

请输入学生人数:3 请输入第1个学生成绩:90 请输入第2个学生成绩:abc 成绩必须是整数,请重新输入 请输入第2个学生成绩:80 请输入第3个学生成绩:59 ===== 成绩统计 ===== 最高分:90 最低分:59 平均分:76.33 及格人数:2 及格率:66.67%

二、先做最小版本

第一版不处理非法输入,只跑通流程。

importjava.util.Scanner;publicclassScoreStatisticsAppV1{publicstaticvoidmain(String[]args){Scannerscanner=newScanner(System.in);System.out.print("请输入学生人数:");intcount=scanner.nextInt();int[]scores=newint[count];for(inti=0;i<count;i++){System.out.print("请输入第"+(i+1)+"个学生成绩:");scores[i]=scanner.nextInt();}intmax=scores[0];intmin=scores[0];intsum=0;intpassCount=0;for(intscore:scores){if(score>max){max=score;}if(score<min){min=score;}if(score>=60){passCount++;}sum+=score;}doubleaverage=sum*1.0/count;doublepassRate=passCount*100.0/count;System.out.println("最高分:"+max);System.out.println("最低分:"+min);System.out.println("平均分:"+average);System.out.println("及格人数:"+passCount);System.out.println("及格率:"+passRate+"%");}}

这个版本能跑,但有问题:

  • 学生人数如果输入 0,会访问scores[0]出错。
  • 成绩如果输入 abc,会直接异常。
  • 成绩如果输入 200,也会被接受。
  • main 太长。
  • 统计逻辑没有拆方法。

项目开发通常就是这样:先跑通,再逐步加校验和结构。

三、设计程序结构

我们把程序拆成几类方法:

读取学生人数 读取一个合法成绩 判断字符串是否整数 查找最高分 查找最低分 计算平均分 统计及格人数 打印统计结果

对应方法:

readStudentCount readScore isInteger findMax findMin calculateAverage countPassed printReport

拆方法不是为了显得高级,而是为了让每段逻辑有名字、有边界。

四、完整版本代码

importjava.util.Scanner;publicclassScoreStatisticsApp{publicstaticvoidmain(String[]args){Scannerscanner=newScanner(System.in);intstudentCount=readStudentCount(scanner);int[]scores=newint[studentCount];for(inti=0;i<studentCount;i++){scores[i]=readScore(scanner,i+1);}printReport(scores);}publicstaticintreadStudentCount(Scannerscanner){while(true){System.out.print("请输入学生人数:");Stringtext=scanner.nextLine();if(!isInteger(text)){System.out.println("学生人数必须是整数,请重新输入");continue;}intcount=Integer.parseInt(text);if(count<=0){System.out.println("学生人数必须大于0,请重新输入");continue;}returncount;}}publicstaticintreadScore(Scannerscanner,intindex){while(true){System.out.print("请输入第"+index+"个学生成绩:");Stringtext=scanner.nextLine();if(!isInteger(text)){System.out.println("成绩必须是整数,请重新输入");continue;}intscore=Integer.parseInt(text);if(score<0||score>100){System.out.println("成绩必须在0到100之间,请重新输入");continue;}returnscore;}}publicstaticbooleanisInteger(Stringtext){if(text==null||text.isEmpty()){returnfalse;}for(inti=0;i<text.length();i++){charch=text.charAt(i);if(ch<'0'||ch>'9'){returnfalse;}}returntrue;}publicstaticintfindMax(int[]scores){intmax=scores[0];for(intscore:scores){if(score>max){max=score;}}returnmax;}publicstaticintfindMin(int[]scores){intmin=scores[0];for(intscore:scores){if(score<min){min=score;}}returnmin;}publicstaticdoublecalculateAverage(int[]scores){intsum=0;for(intscore:scores){sum+=score;}returnsum*1.0/scores.length;}publicstaticintcountPassed(int[]scores){intcount=0;for(intscore:scores){if(score>=60){count++;}}returncount;}publicstaticvoidprintReport(int[]scores){intmax=findMax(scores);intmin=findMin(scores);doubleaverage=calculateAverage(scores);intpassCount=countPassed(scores);doublepassRate=passCount*100.0/scores.length;System.out.println();System.out.println("===== 成绩统计 =====");System.out.println("最高分:"+max);System.out.println("最低分:"+min);System.out.printf("平均分:%.2f%n",average);System.out.println("及格人数:"+passCount);System.out.printf("及格率:%.2f%%%n",passRate);}}

五、逐段解释

1. main 方法只保留主流程

intstudentCount=readStudentCount(scanner);int[]scores=newint[studentCount];for(inti=0;i<studentCount;i++){scores[i]=readScore(scanner,i+1);}printReport(scores);

main 读起来像流程说明:

读取学生人数 -> 创建成绩数组 -> 逐个读取成绩 -> 打印报告

这就是抽方法的好处。

2. readStudentCount 为什么用 while true

while(true){...if(不合法){continue;}returncount;}

这里的逻辑是:只要用户没输入合法人数,就一直要求重新输入。一旦合法,return count直接结束方法。

这种循环适合“直到输入合法”的场景。

3. isInteger 为什么自己写

我们先不用异常处理,是因为异常还没正式讲。第一阶段用字符判断更直观:

for(inti=0;i<text.length();i++){charch=text.charAt(i);if(ch<'0'||ch>'9'){returnfalse;}}

它逐个检查字符是否在'0''9'之间。

这个方法当前不支持负数,因为学生人数和成绩都不应该是负数。

4. findMax 和 findMin 为什么用 scores[0]

intmax=scores[0];

因为如果分数范围以后变了,初始值写死可能不可靠。用数组第一个元素作为初始值更通用。

本项目里学生人数已经保证大于 0,所以 scores 一定不是空数组。

5. printf 中的%%

System.out.printf("及格率:%.2f%%%n",passRate);

%.2f表示保留两位小数。
%%表示输出一个百分号。
%n表示换行。

所以这里会输出:

及格率:66.67%

六、这个项目用到了哪些知识

知识在项目中的体现
变量studentCount、scores、max、min
类型int、double、String、boolean、int[]
输入Scanner
输出print、println、printf
条件校验人数、校验成绩
循环重复输入、遍历数组
continue输入不合法时重新循环
return输入合法后返回结果
方法拆分读取、统计、输出
数组保存多个成绩

这就是阶段项目的意义:把分散知识串起来。

七、可以继续改进的地方

这个项目还不是最终工程。它还有很多可以改进的地方:

  • 支持输入学生姓名。
  • 每个学生不仅有成绩,还有学号。
  • 统计优秀人数。
  • 保存到文件。
  • 下次启动读取历史成绩。
  • 用类表示 Student。
  • 用 List 替代数组。
  • 用异常处理输入转换。

这些改进会在后续阶段逐步实现。现在不要急着一次做完。学习编程最重要的是每一步都知道自己为什么这么写。

八、常见错误

1.scanner.nextLine()读到空字符串

如果你混用nextInt()nextLine(),容易出现换行残留。本项目统一使用nextLine(),避免这个问题。

2. 空数组访问 scores[0]

如果学生人数允许 0,findMax会出错。本项目在readStudentCount保证人数大于 0。

3. 平均分整数除法

错误:

returnsum/scores.length;

正确:

returnsum*1.0/scores.length;

4. 忘记 return 导致循环停不下来

合法输入后必须 return,否则 while true 会继续。

5. 方法拆太碎或不拆

所有逻辑堆 main 里难读;但每一行都拆方法也没必要。拆方法的原则是:这段逻辑有明确名字,并且能独立理解。

九、本章练习

  1. 增加优秀人数统计:分数大于等于 90 算优秀。

  2. 增加不及格人数统计。

  3. 修改输出,让平均分和及格率都保留 1 位小数。

  4. 增加输入班级名称,并在报告中输出。

  5. 尝试把printReport再拆成:

printBasicReport printPassReport
  1. 思考:如果要保存学生姓名和成绩,数组还够不够用?

十、第一阶段总结

完成这个项目,说明你已经能把第一阶段的基础知识串起来。

你应该已经具备:

  • 编写 main 程序。
  • 使用变量和类型。
  • 从命令行读取输入。
  • 使用条件校验数据。
  • 使用循环重复处理。
  • 使用数组保存一组数据。
  • 使用方法拆分逻辑。
  • 阅读基础错误并修复。

这还不是 Java 的全部,但它是继续学习面向对象的前提。下一阶段会把“学生成绩”这种散落的数据升级成对象,比如Student类。到那时,你会看到 Java 为什么总是强调类和对象。

http://www.jsqmd.com/news/1075807/

相关文章:

  • 四门超级跑车Star Matrix
  • 2026年高效利器:抖音、B站、简书图文内容一键自动分发软件
  • GitHub Desktop中文界面一键汉化指南:告别英文困扰,享受母语开发体验
  • 5个实用技巧:使用Dism++免费系统优化工具快速提升电脑性能
  • 手把手教你kaliMSFAPK木马制作和捆绑
  • 如何用纯前端技术构建Windows 12网页版:从概念到实现的完整指南
  • ArcObjects SDK 10.8终极指南:如何快速掌握GIS开发核心技术
  • 文件上传漏洞实战:从upload-labs靶场到安全防御全解析
  • 【DDS】入门基础
  • 代码注入与内存操作:从原理到实战的逆向工程核心技术
  • 3分钟终极指南:如何免费激活Windows和Office的完整教程
  • Visual C++ Redistributable AIO:一键解决Windows程序运行问题的完整指南
  • 汽车网关演进:从CAN总线到以太网骨干的架构与安全实践
  • 显存不够用,ROCm 7.x 下 vLLM 量化与重计算策略实战效果
  • Immich:自己搭一个照片管理平台,10 万 Star 了
  • 第 20 篇:会话维持(Session)—— 爬虫的“身份系统“
  • 第10章 封装:让对象保护自己的规则
  • 基于RAG的新闻电影感叙事发现系统设计与实践
  • 2026标杆企业参观游学怎么选?头部参访、跨行业研学全指南~
  • LlamaIndex、LangChain与smolagent生产选型实战指南
  • 最新推荐 AI 量化工具前,先问要解决哪段问题
  • 【路径规划】改进的SCA算法多机器人路径规划【含Matlab源码 15659期】
  • AMAT 0190-B9760真空控制器
  • 【招聘】第一篇:分布式招聘:为什么你的人才管道总是在最需要的时候断掉
  • 上门按摩平台换了一种运营方式,结果差了这么多
  • FFmpegGUI:5个步骤让专业视频处理变得像搭积木一样简单
  • Mate Engine终极指南:如何在5分钟内打造你的专属虚拟桌面伴侣
  • AUTOSAR 完整深度详解
  • ADC 笔记 —— STM32 标准库实现
  • 2026年零基础看量化代码,先用小策略缩小练习范围