基于MVC模式开发带算式显示的Java Swing计算器
基于MVC模式开发带算式显示的Java Swing计算器
在Java桌面应用开发中,Swing是构建图形界面的常用工具,而MVC(Model-View-Controller)架构模式则能让代码结构更清晰、可维护性更强。本文将详细介绍如何使用MVC模式开发一款支持算式实时显示的简易计算器,解决基础计算器无法直观查看输入表达式的问题。
一、开发背景与核心需求
日常使用的简易计算器常存在一个痛点:只能显示当前输入的单个数字,无法查看完整的计算表达式(如“12×3+45÷5”),导致用户容易输入错误且难以核对。基于此,我们确定以下核心需求:
- 图形界面支持数字、运算符(+、-、×、÷)、括号(())输入
- 实时显示完整计算表达式
- 支持运算符优先级计算(先乘除后加减,括号优先)
- 提供清空(C)、退格(←)等便捷操作
- 结果显示自动优化(整数去除小数点后多余的0)
二、MVC架构设计思路
MVC模式通过将应用拆分为三个独立模块,实现“关注点分离”,具体分工如下:
| 模块 | 职责 | 对应计算器功能 |
|---|---|---|
| Model(模型) | 存储数据(计算表达式)、实现业务逻辑(表达式解析与计算) | 管理输入的算式字符串、处理加减乘除运算、解决运算符优先级 |
| View(视图) | 负责界面展示与用户交互 | 绘制计算器窗口、按钮、表达式显示框、结果显示框 |
| Controller(控制器) | 协调Model与View,传递用户操作与数据更新 | 监听按钮点击事件、调用Model处理数据、通知View更新显示 |
这种架构的优势在于:后续修改界面(如更换按钮样式)无需改动计算逻辑,优化运算规则也无需调整界面代码,极大提升了代码可维护性。
三、完整实现代码与模块解析
1. 项目结构
整个计算器项目仅需一个Java文件(Calculator.java),内部包含4个核心类:
CalculatorModel:模型类,处理数据与计算CalculatorView:视图类,构建图形界面CalculatorController:控制器类,协调交互Calculator:主类,程序入口
2. 模块详细实现
(1)Model:核心计算逻辑
模型类是计算器的“大脑”,负责存储表达式和实现计算算法。这里采用逆波兰表达式(后缀表达式) 解决运算符优先级问题,支持括号、小数运算。
class CalculatorModel {
private String currentExpression = ""; // 存储完整计算表达式
// 添加内容到表达式(数字、运算符、括号)
public void appendToExpression(String s) {
currentExpression += s;
}
// 清空表达式
public void clearExpression() {
currentExpression = "";
}
// 退格:删除最后一个字符
public void backspace() {
if (!currentExpression.isEmpty()) {
currentExpression = currentExpression.substring(0, currentExpression.length() - 1);
}
}
// 获取当前表达式(供View显示)
public String getExpression() {
return currentExpression;
}
// 计算表达式结果(核心方法)
public String calculate() {
if (currentExpression.isEmpty()) return "0";
try {
// 替换中文运算符为Java支持的符号(×→*,÷→/)
String expr = currentExpression.replace("×", "*").replace("÷", "/");
// 调用逆波兰表达式算法计算结果
double result = evaluateExpression(expr);
// 格式化结果:整数显示为long型(如15.0→15),小数正常显示
return result % 1 == 0 ? String.valueOf((long) result) : String.valueOf(result);
} catch (Exception e) {
return "错误"; // 处理计算异常(如除数为0、表达式语法错误)
}
}
// 逆波兰表达式计算(处理运算符优先级)
private double evaluateExpression(String expr) {
Stack<Double> numStack = new Stack<>(); // 存储数字的栈
Stack<Character> opStack = new Stack<>(); // 存储运算符的栈
expr = expr.replaceAll("\\s+", ""); // 去除表达式中的空格
for (int i = 0; i < expr.length(); i++) {
char c = expr.charAt(i);
// 1. 处理数字和小数点(支持多位数、小数)
if (Character.isDigit(c) || c == '.') {
StringBuilder num = new StringBuilder();
// 读取连续的数字和小数点
while (i < expr.length() && (Character.isDigit(expr.charAt(i)) || expr.charAt(i) == '.')) {
num.append(expr.charAt(i++));
}
i--; // 回退一位,避免跳过下一个字符
numStack.push(Double.parseDouble(num.toString()));
}
// 2. 处理左括号:直接入栈
else if (c == '(') {
opStack.push(c);
}
// 3. 处理右括号:计算到左括号为止
else if (c == ')') {
while (opStack.peek() != '(') {
// 弹出两个数字和一个运算符计算,结果入栈
numStack.push(calculateSingleOp(numStack.pop(), numStack.pop(), opStack.pop()));
}
opStack.pop(); // 弹出左括号(不参与计算)
}
// 4. 处理运算符:按优先级入栈(乘除优先级高于加减)
else {
// 若栈顶运算符优先级更高,先计算栈内表达式
while (!opStack.isEmpty() && getOpPriority(opStack.peek()) >= getOpPriority(c)) {
numStack.push(calculateSingleOp(numStack.pop(), numStack.pop(), opStack.pop()));
}
opStack.push(c); // 当前运算符入栈
}
}
// 5. 处理栈中剩余的运算符
while (!opStack.isEmpty()) {
numStack.push(calculateSingleOp(numStack.pop(), numStack.pop(), opStack.pop()));
}
return numStack.pop(); // 最终结果
}
// 计算单个运算符(注意:栈弹出顺序是“后入的数字在前”)
private double calculateSingleOp(double b, double a, char op) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/':
if (b == 0) throw new ArithmeticException("除数不能为0");
return a / b;
default: throw new IllegalArgumentException("无效运算符:" + op);
}
}
// 定义运算符优先级:乘除(2)> 加减(1)> 其他(0)
private int getOpPriority(char op) {
return switch (op) {
case '+', '-' -> 1;
case '*', '/' -> 2;
default -> 0;
};
}
}
(2)View:图形界面实现
视图类使用Swing组件构建计算器界面,包含两个核心显示区域(表达式框、结果框)和一个4×5的按钮面板,布局采用BorderLayout和GridLayout组合,确保界面美观且自适应。
class CalculatorView extends JFrame {
private JTextField expressionField; // 显示完整表达式(上方)
private JTextField resultField; // 显示计算结果(下方)
private JPanel buttonPanel; // 按钮面板
// 构造方法:初始化界面
public CalculatorView() {
setTitle("带算式显示的计算器"); // 窗口标题
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口时退出程序
setLayout(new BorderLayout(5, 5)); // 边框布局,组件间距5px
initComponents(); // 初始化组件
pack(); // 自动调整窗口大小以适应组件
setLocationRelativeTo(null); // 窗口居中显示
setResizable(false); // 禁止调整窗口大小
}
// 初始化所有组件
private void initComponents() {
// 1. 表达式显示框(只读,右对齐,小字体)
expressionField = new JTextField();
expressionField.setEditable(false); // 禁止用户手动编辑
expressionField.setHorizontalAlignment(JTextField.RIGHT); // 文本右对齐
expressionField.setFont(new Font("Arial", Font.PLAIN, 16)); // 字体样式
expressionField.setPreferredSize(new Dimension(350, 40)); // 固定尺寸
// 2. 结果显示框(只读,右对齐,大字体)
resultField = new JTextField("0"); // 默认显示0
resultField.setEditable(false);
resultField.setHorizontalAlignment(JTextField.RIGHT);
resultField.setFont(new Font("Arial", Font.BOLD, 24)); // 加粗字体,更醒目
resultField.setPreferredSize(new Dimension(350, 50));
// 3. 显示区域面板(组合表达式框和结果框)
JPanel displayPanel = new JPanel(new BorderLayout());
displayPanel.add(expressionField, BorderLayout.NORTH);
displayPanel.add(resultField, BorderLayout.SOUTH);
add(displayPanel, BorderLayout.NORTH); // 显示区域放在窗口顶部
// 4. 按钮面板(4行5列网格布局)
buttonPanel = new JPanel(new GridLayout(4, 5, 3, 3)); // 行列间距3px
// 按钮文本顺序(按计算器常用布局排列)
String[] buttonTexts = {
"7", "8", "9", "÷", "C",
"4", "5", "6", "×", "←",
"1", "2", "3", "-", "=",
"0", ".", "+", "(", ")"
};
// 循环创建按钮并添加到面板
for (String text : buttonTexts) {
JButton btn = new JButton(text);
btn.setFont(new Font("Arial", Font.PLAIN, 18)); // 按钮字体
buttonPanel.add(btn);
}
add(buttonPanel, BorderLayout.CENTER); // 按钮面板放在窗口中间
}
// 更新表达式显示(供Controller调用)
public void setExpression(String expr) {
expressionField.setText(expr);
}
// 更新结果显示(供Controller调用)
public void setResult(String result) {
resultField.setText(result);
}
// 为所有按钮添加点击监听器(供Controller绑定事件)
public void addButtonListener(ActionListener listener) {
Component[] components = buttonPanel.getComponents();
for (Component comp : components) {
if (comp instanceof JButton) {
((JButton) comp).addActionListener(listener);
}
}
}
}
(3)Controller:交互逻辑协调
控制器类是Model与View之间的“桥梁”,通过监听View的按钮点击事件,调用Model的对应方法处理数据,再通知View更新显示,实现“解耦”。
class CalculatorController implements ActionListener {
private CalculatorModel model; // 持有Model引用
private CalculatorView view; // 持有View引用
// 构造方法:绑定Model和View
public CalculatorController(CalculatorModel model, CalculatorView view) {
this.model = model;
this.view = view;
this.view.addButtonListener(this); // 为View的按钮添加监听器
}
// 处理按钮点击事件
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand(); // 获取点击按钮的文本
switch (command) {
// 1. 清空按钮(C):重置表达式和结果
case "C":
model.clearExpression();
view.setExpression("");
view.setResult("0");
break;
// 2. 等于按钮(=):计算结果并显示
case "=":
String result = model.calculate();
view.setResult(result);
break;
// 3. 退格按钮(←):删除表达式最后一个字符
case "←":
model.backspace();
view.setExpression(model.getExpression());
break;
// 4. 其他按钮(数字、运算符、小数点、括号):添加到表达式
default:
model.appendToExpression(command);
view.setExpression(model.getExpression());
break;
}
}
}
(4)主类:程序入口
主类通过SwingUtilities.invokeLater()在Swing事件调度线程中创建界面,避免多线程安全问题。
public class Calculator {
public static void main(String[] args) {
// 在Swing事件线程中创建界面(推荐写法,确保线程安全)
SwingUtilities.invokeLater(() -> {
CalculatorModel model = new CalculatorModel(); // 创建模型
CalculatorView view = new CalculatorView(); // 创建视图
new CalculatorController(model, view); // 创建控制器(绑定Model和View)
view.setVisible(true); // 显示窗口
});
}
}
四、功能测试与使用说明
1. 编译与运行
- 编译:在命令行进入代码所在目录,执行
javac Calculator.java - 运行:执行
java Calculator,即可打开计算器窗口
2. 核心功能测试



3. 使用技巧
- 输入负数:可通过括号实现,如
(3-5)(结果为-2) - 修正输入:若输错字符,点击“←”删除最后一个字符,无需重新输入全部表达式
五、代码优化与扩展方向
当前计算器已实现核心功能,若需进一步完善,可考虑以下方向:
- 添加历史记录:在Model中增加
List<String>存储历史计算记录,View中添加滚动面板显示历史记录 - 支持更多运算:增加平方(x²)、平方根(√)、取余(%)等运算符,需在Model中扩展
evaluateExpression方法 - 界面美化:使用
UIManager设置系统外观(如Windows风格),或自定义按钮颜色、背景图片 - 键盘支持:添加键盘事件监听,支持用户通过键盘输入数字和运算符(如按Enter键计算结果)
- 表达式语法校验:在Model中增加输入校验逻辑(如避免连续输入运算符),提升用户体验
六、总结
本文通过MVC模式开发的Java Swing计算器,不仅实现了“算式实时显示”这一核心需求,还通过架构拆分让代码逻辑更清晰。MVC模式的优势在项目中体现得淋漓尽致:
- Model专注于数据与计算,无需关心界面
- View仅负责显示,不包含任何业务逻辑
- Controller统一处理交互,避免Model与View直接耦合
这种设计思路不仅适用于计算器,也可推广到其他Java桌面应用开发中。希望本文能为刚接触Swing或MVC模式的开发者提供参考,帮助大家写出更易维护、可扩展的代码。
以上就是博客的全部文本内容啦。如果你对内容有修改需求,比如增减某些技术细节、调整语言风格,或者想补充代码运行截图的说明等,都可以随时告诉我。

浙公网安备 33010602011771号