ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java Swing图形界面计算器课程设计:从界面布局到计算逻辑完整实现

Java Swing图形界面计算器课程设计:从界面布局到计算逻辑完整实现 简介一份Java课程设计完整文档面向计算机相关专业学生与Java初学者围绕图形界面计算器展开涵盖加法、减法、乘法、除法、取余等基本运算及小数点、正负号、求倒数、退格、清零功能并扩展了开平方、平方、立方、素数判断与对数计算。文档按课设任务及要求、需求分析、设计思路、详细设计、运行调试与分析讨论、设计体会与小结等结构组织讲解了BorderLayout与GridLayout布局应用、LinkedList存储表达式数据、事件监听器处理按钮输入等关键实现适合作为课程设计参考或期末作业模板。资源为单份doc文档整体346KB已有1500人学习下载。文档包含完整目录、源码类关系说明与运行效果展示读者可据此梳理GUI编程、事件驱动模型和数据结构在Java项目中的实际用法亦可直接参考其框架完成同类计算器设计。 把Java图形界面计算器做成课程设计是很多计算机专业学生绕不过去的一道坎。题目看起来就一句话——“设计一个图形界面的计算器完成简单的算术运算”但真正动手才发现界面怎么摆、按钮怎么响应、等号背后的逻辑怎么处理每一步都有坑等着你。这篇文章把我自己做完这个项目、以及帮学弟学妹改代码过程中踩过的雷和总结的经验完整写出来包含可直接运行的完整代码、设计思路和报告组织方式适合正在做Java课程设计、准备期末项目、或者想系统补一补Swing和事件处理基础的同学参考。1. “简单算术运算”这五个字信息量其实很大1.1 这题到底在考什么能力很多同学拿到题目第一反应是计算器有什么难的不就是放一堆按钮点一下显示一下吗。真做起来才知道这道题覆盖了Java课程里最重要的几块内容Swing图形界面搭建、布局管理器的使用、事件监听机制、字符串与数字的处理、异常情况兜底。老师出这道题本质上是想通过一个完整的小项目看你能否把课堂上学到的零散知识点串成一个能跑、能看、能用的东西。从评分角度看界面美观和交互合理往往占30%左右计算逻辑正确占40%左右代码规范和报告质量占30%左右。所以别只顾着赶功能界面布局、按钮风格、异常提示这些“软性指标”同样影响最终分数。1.2 动工之前先列功能清单一个能交作业的计算器功能至少要覆盖以下几点能输入0-9数字能输入小数点支持加、减、乘、除四种运算支持连续输入比如 12.5 3 * 4 - 2等号触发求值结果展示在显示框清空键C和退格键用于修正输入输入不合法或除数为0时给出错误提示而不是程序直接崩掉把这六条写在纸上再动手后面写代码就有的放矢。很多同学写到一半不知道下一步干什么往往就是没做这个功能拆解。2. 界面层用Swing把计算器“画”出来2.1 顶层窗口与显示框计算器的窗口用JFrame整体布局采用BorderLayout上方放显示框中间放按钮区域。显示框不要用JLabel用JTextField并设为不可编辑更合适这样既显示内容又防止用户直接用键盘乱输。显示框有几个细节值得注意字体要大建议20到28磅右对齐和真实计算器的显示习惯一致。边框留出内边距让数字不要紧紧贴在框边上这些细节改起来成本极低但视觉效果提升非常明显。display new JTextField(0); display.setFont(new Font(微软雅黑, Font.BOLD, 28)); display.setHorizontalAlignment(JTextField.RIGHT); display.setEditable(false); display.setBackground(Color.WHITE); display.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));2.2 按钮区域与布局选择按钮区域推荐用GridLayout也就是网格布局。比如四行四列的网格放16个按钮或者五行四列放20个按钮。手写计算器一般五行四列从上到下依次是功能键、数字键、运算符和等号。我建议把按钮分成几个逻辑区最上面一排放C、退格、除号、乘号中间三排放数字和加减右下角放等号。等号按钮用不同的背景色突出比如蓝色背景白色文字。这样布局清晰老师打开程序第一眼就感觉东西做完了不是随便堆上去的。GridLayout每个格子大小相同JButton会自动填满格子。为了让按钮之间有间距可以在GridLayout构造时传入水平和垂直间距buttonPanel.setLayout(new GridLayout(5, 4, 6, 6));按钮上的字体也要统一设置默认字体在放大后经常出现显示不全的问题。2.3 这些UI细节老师一眼就能看出用心程序启动时窗口居中显示用setLocationRelativeTo(null)窗口大小要适配内容建议360x480左右按钮点击后不要有焦点虚框设置setFocusPainted(false)换系统外观UIManager.setLookAndFeel(...)否则Swing默认的Metal风格在Windows上很难看这些细节都不难但组合起来就是“一看就是认真做的”和“随手糊的”之间的差别。3. 事件层按钮怎么知道该干什么3.1 监听器与getActionCommandSwing按钮的点击事件通过ActionListener处理。最简单的方式是给每个按钮单独写一个监听器但20个按钮写20个监听器类代码太冗余。更聪明的做法是让所有按钮共享一个监听方法通过e.getActionCommand()拿到当前被点击按钮的文字再分支处理private void onButtonClick(ActionEvent e) { String cmd e.getActionCommand(); if (cmd.matches(\\d) || cmd.equals(.)) { // 处理数字和小数点 } else if (cmd.equals(C)) { // 清空 } else if (cmd.equals()) { // 求值 } else { // 运算符 } }Java 8以后可以用方法引用或Lambda让代码更简洁比如btn.addActionListener(this::onButtonClick)。不过要注意getActionCommand()返回的是按钮上显示的文本所以按钮文字不要乱写它既是显示内容也是判断分支的依据。3.2 输入规则的约束比计算本身更重要计算器60%的bug不是出在算法上而是出在“用户乱点”上。比如连续点两个运算符2 * 3要不要拦下来开头就点乘号除号* 2算什么一个数字里出现两个小数点2.3.4合不合法合理的做法是写一个状态判断当前表达式最后一位是不是运算符。如果是再输入运算符时直接替换而不是追加。数字里如果已有小数点再次输入小数点时忽略。开头只允许数字或负号不允许乘号除号。这些规则虽然简单但能避免大量后面在计算层的麻烦。4. 计算层等号背后最容易翻车的地方4.1 两种实现思路的取舍计算逻辑有两条路线。第一条不保存表达式每输入一个运算符就立刻算比如23就在按时算5。这样实现简单代码短但没法处理优先级——按2、、3、、4时如果按就先算成了5结果4就会出错。要支持运算符优先级这条路线基本走不通。第二条把所有输入保存成一个表达式字符串按等号时统一解析求值。这条路线能正确处理优先级代码也干净推荐采用。为了处理乘除优先于加减可以用“两遍扫描”的思路第一遍只处理*和/第二遍只处理和-。这是最简单、也最容易向老师解释的优先级实现方案在答辩时直接说“用两遍遍历完成运算符优先级处理”比用递归下降或逆波兰表达式更容易讲清楚。4.2 用两遍扫描处理运算符优先级具体做法先把表达式字符串拆成一个列表元素交替为数字、运算符、数字、运算符……这样形成一个线性结构然后先遍历乘除把数字*数字或数字/数字替换成计算结果再遍历加减。这里有个实现细节值得单独说遍历时如果跳过中间元素很容易出现“删错位置”的索引问题。我调试时踩过几次坑总结出一个可靠写法在乘除遍历中把i从0开始遇到运算符是*或/时取它左右两边的数字计算出结果用结果覆盖左操作数位置然后连续移除运算符和右操作数再将i回退一位。回退是为了补偿for循环的i否则会跳过一个元素导致漏算。for (int i 0; i tokens.size(); i) { String t tokens.get(i); if (t.equals(*) || t.equals(/)) { double a Double.parseDouble(tokens.get(i - 1)); double b Double.parseDouble(tokens.get(i 1)); double val t.equals(*) ? a * b : a / b; tokens.set(i - 1, String.valueOf(val)); tokens.remove(i); tokens.remove(i); i--; } }这段逻辑我第一次写时因为忘记i--连续乘除总是只算第一个8/4/2算出2而不是1。这个问题排查了很久后来在草稿纸上模拟了一遍才找到原因。4.3 浮点数精度问题与格式化double做浮点运算是Java课程里必考的坑0.10.2的结果不是0.3而是0.30000000000000004。计算器是给人用的工具必须把这种尾巴清理掉。常用两种方案一种是用BigDecimal做精确计算但乘除法还要考虑精度和舍入模式代码麻烦另一种是计算完用DecimalFormat格式化课程设计级别用这个足够。DecimalFormat df new DecimalFormat(#.##########); String text df.format(result);这样0.30000000000000004会显示成0.3。但注意DecimalFormat对大数会输出科学计数法比如1.23456789E12这是正常现象一般不需要干预。如果担心可以在报告里说明“超过一定位数的结果使用科学计数法显示”这反而是个加分项说明你注意到了边界情况。5. 完整代码与课程设计报告的写作要点5.1 可直接运行的完整代码下面这份代码是我整理后的版本去掉了一些花哨功能保留了核心能力支持四则运算、支持小数、支持连续计算、支持优先级、支持退格和清空、除数为0时提示错误。在JDK 8及以上环境都能直接编译运行。import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; public class Calculator extends JFrame { private JTextField display; private StringBuilder expression new StringBuilder(); public Calculator() { setTitle(Java课程设计 - 图形界面计算器); setSize(360, 480); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setLayout(new BorderLayout()); display new JTextField(0); display.setFont(new Font(微软雅黑, Font.BOLD, 28)); display.setHorizontalAlignment(JTextField.RIGHT); display.setEditable(false); display.setBackground(Color.WHITE); display.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); add(display, BorderLayout.NORTH); JPanel buttonPanel new JPanel(); buttonPanel.setLayout(new GridLayout(5, 4, 6, 6)); buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); String[] labels { C, ⌫, /, *, 7, 8, 9, -, 4, 5, 6, , 1, 2, 3, , 0, ., 00, }; for (String label : labels) { JButton btn new JButton(label); btn.setFont(new Font(微软雅黑, Font.PLAIN, 20)); btn.setFocusPainted(false); if (label.equals()) { btn.setBackground(new Color(66, 133, 244)); btn.setForeground(Color.WHITE); } else if (label.equals(C) || label.equals(⌫)) { btn.setBackground(new Color(242, 242, 242)); btn.setForeground(new Color(180, 30, 30)); } btn.addActionListener(this::onButtonClick); buttonPanel.add(btn); } add(buttonPanel, BorderLayout.CENTER); // 绑定键盘输入 display.addKeyListener(new java.awt.event.KeyAdapter() { Override public void keyPressed(java.awt.event.KeyEvent e) { String key String.valueOf(e.getKeyChar()); if (key.matches([0-9.*/-]) || key.equals(.)) { processInput(key); } else if (e.getKeyCode() java.awt.event.KeyEvent.VK_ENTER) { processInput(); } else if (e.getKeyCode() java.awt.event.KeyEvent.VK_BACK_SPACE) { processInput(⌫); } } }); } private void onButtonClick(ActionEvent e) { processInput(e.getActionCommand()); } private void processInput(String cmd) { try { if (cmd.matches(\\d) || cmd.equals(.)) { appendNumber(cmd); } else if (cmd.equals(C)) { expression.setLength(0); display.setText(0); } else if (cmd.equals(⌫)) { if (expression.length() 0) { expression.deleteCharAt(expression.length() - 1); updateDisplay(); } } else if (cmd.equals()) { calculate(); } else if (cmd.equals() || cmd.equals(-) || cmd.equals(*) || cmd.equals(/)) { appendOperator(cmd); } } catch (Exception ex) { display.setText(错误); expression.setLength(0); } } private void appendNumber(String num) { String current expression.toString(); // 如果末尾是运算符则开始新的数字输入 if (expression.length() 0) { char last current.charAt(current.length() - 1); // 统计当前数字中小数点的个数 int dotIndex -1; for (int i current.length() - 1; i 0; i--) { char c current.charAt(i); if (c || c - || c * || c /) { break; } if (c .) { dotIndex i; break; } } // 小数点后不能再输入小数点 if (num.equals(.) dotIndex ! -1) { return; } } expression.append(num); updateDisplay(); } private void appendOperator(String op) { if (expression.length() 0) { if (op.equals(-)) { expression.append(-); updateDisplay(); } return; } char last expression.charAt(expression.length() - 1); if (last || last - || last * || last /) { expression.setCharAt(expression.length() - 1, op.charAt(0)); } else { expression.append(op); } updateDisplay(); } private void updateDisplay() { if (expression.length() 0) { display.setText(0); } else { display.setText(expression.toString()); } } private void calculate() { if (expression.length() 0) { return; } String expr expression.toString(); char last expr.charAt(expr.length() - 1); if (last || last - || last * || last /) { expr expr.substring(0, expr.length() - 1); } if (expr.length() 0) { return; } double result evaluate(expr); DecimalFormat df new DecimalFormat(#.##########); String text df.format(result); display.setText(text); expression.setLength(0); expression.append(text); } private static double evaluate(String expr) { if (expr.isEmpty()) { throw new IllegalArgumentException(表达式为空); } char first expr.charAt(0); if (!Character.isDigit(first) first ! .) { throw new IllegalArgumentException(表达式格式错误); } ListString tokens new ArrayList(); StringBuilder num new StringBuilder(); for (int i 0; i expr.length(); i) { char c expr.charAt(i); if (Character.isDigit(c) || c .) { num.append(c); } else { tokens.add(num.toString()); num.setLength(0); tokens.add(String.valueOf(c)); } } tokens.add(num.toString()); // 第一遍乘除 for (int i 0; i tokens.size(); i) { String t tokens.get(i); if (t.equals(*) || t.equals(/)) { double a Double.parseDouble(tokens.get(i - 1)); double b Double.parseDouble(tokens.get(i 1)); if (t.equals(/) Math.abs(b) 1e-10) { throw new ArithmeticException(除数不能为0); } double val t.equals(*) ? a * b : a / b; tokens.set(i - 1, String.valueOf(val)); tokens.remove(i); tokens.remove(i); i--; } } // 第二遍加减 double result Double.parseDouble(tokens.get(0)); for (int i 1; i tokens.size(); i 2) { String op tokens.get(i); double val Double.parseDouble(tokens.get(i 1)); if (op.equals()) { result val; } else if (op.equals(-)) { result - val; } } return result; } public static void main(String[] args) { SwingUtilities.invokeLater(() - { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ignored) { } new Calculator().setVisible(true); }); } }说几个代码里的设计为什么表达式末尾是运算符时按等于要先把末尾运算符截掉再算因为用户可能输入到一半直接按等号截掉后至少不会解析报错这也算一种友好兜底。为什么数字按钮的正则要用\\d而不是手写判断因为两位数以上的数字靠一次一次点击组合每位都是0-9的单个数字正则匹配更简洁。为什么除零判断用Math.abs(b) 1e-10而不是b 0因为浮点数除以绝对值很小的数结果会溢出成Infinity提前判断能避免显示乱七八糟的东西。5.2 课程设计报告该怎么组织报告写得好答辩压力小一大半。计算器这份报告建议按这个顺序组织需求分析功能清单概要设计说明用了MVC的思想界面层、事件层、计算层分离详细设计贴关键代码片段逐一解释测试报告列出测试用例和结果总结与心得。我看了很多份课程设计报告最容易踩的坑是“Hello World级别”的详细设计整页贴上完整代码却没有一句解释。好的做法是贴关键方法的代码用文字说明设计意图比如“这里采用两遍扫描是为了处理乘除优先级”让老师看出你真正理解了原理。6. 测试、常见问题与答辩加分6.1 照着这个清单测一遍再交我建议交作业前至少跑完下面这组用例也建议写进报告的测试章节测试内容输入预期结果加法12 34 46减法5 - 8 -3乘法6 * 7 42除法9 / 2 4.5除数为08 / 0 错误提示优先级2 3 * 4 14连续乘除8 / 4 / 2 1小数0.1 0.2 0.3连续运算10 - 3 * 2 5 9替换运算符2 * 32 * 3把这些用例跑完逻辑上的大坑基本都能避开。每组用例写清楚输入、实际输出、是否符合预期报告的测试章节也顺便完工了。6.2 常见问题排查代码跑不起来八成是这几个原因环境变量没配好javac提示找不到命令先去命令行执行java -version确认JDK可用。开发工具里运行报错多半是代码里引用了不存在的类或方法检查import有没有导对。窗口能打开但按钮点了没反应优先查按钮是否添加了监听器其次查getActionCommand()拿到的值是不是空字符串。还有一类问题是显示中文乱码。Swing默认字体在某些系统上对中文支持不好窗口标题和按钮文字变成方框。解决方式是在代码里把字体显式设置为微软雅黑或宋体或者给按钮和窗口都设置指定的Font。窗口标题可以用英文规避但按钮上的中文如果有还是设字体更稳妥。6.3 答辩加分与拓展思路答辩时老师最爱问的问题就那几个提前准备一下为什么用GridLayout为什么用StringBuilder存表达式乘除优先级是怎么处理的除数为0是怎么处理的这些问题在这篇文章里都有答案理解了以后用自己的话说出来就行。交完基础版本还有余力的话可以做两个低成本高回报的扩展一是支持键盘输入给按钮对应的按键绑定事件这个功能也就十来行代码但演示起来很酷二是把两遍扫描换成“中缀转后缀”的逆波兰表示法求值在报告里写“本设计采用两遍扫描处理优先级进一步可扩展为逆波兰表达式以支持括号”既展示了你的知识边界也给自己留了继续深入的接口。答辩时老师听到你能说出“逆波兰”三个字基本就不会再追问太多边界情况了。我自己的体会是这种课程设计最值钱的部分不是最后的分数而是从“界面不知道怎么写”到“能独立把20个按钮的事件串联起来”的整个过程。计算器虽小但界面、事件、逻辑三层各司其职的套路后面做任何Swing项目都能复用。把这份代码彻底弄明白比在网上随便找一份能跑的交上去收获要大得多。本文还有配套的精品资源点击获取
返回列表