Java计算器课程设计
项目概述
本项目旨在使用Java语言和Swing图形用户界面库,设计并实现一个功能完善、界面友好的桌面计算器,该计算器应具备基本的算术运算(加、减、乘、除)、小数点运算、正负号切换、清除功能(C, CE)以及一个简单的内存功能。
需求分析
在开始编码前,我们需要明确计算器需要具备哪些功能。

功能性需求:
- 基本运算:
- 加法 ()
- 减法 ()
- 乘法 ()
- 除法 ()
- 辅助功能:
- 等号 (): 计算并显示结果。
- 小数点 (): 支持浮点数运算。
- 正负号切换 (): 改变当前数字的正负。
- 清除输入: 类似于计算器上的
CE(Clear Entry),清除当前输入的数字,但保留上一次的计算结果。 - 全部清除: 类似于计算器上的
C(Clear),清除所有内容,回到初始状态。 - 退格: 删除最后输入的一位数字。
- 内存功能 (可选,可作为加分项):
- 记忆加 (
M+): 将当前显示的数字加到内存中。 - 记忆减 (
M-): 将当前显示的数字从内存中减去。 - 记忆 recall (
MR): 调出内存中的数值。 - 记忆清除 (
MC): 清空内存。
- 记忆加 (
- 错误处理:
- 除零错误: 当用户尝试除以零时,应显示 "Error" 或类似提示。
- 运算符连续输入: 允许连续输入运算符,后一个运算符覆盖前一个(输入
5 + * 3应视为5 * 3)。 - 数字溢出: 当计算结果超出
double类型的表示范围时,应处理异常。
非功能性需求:
- 界面美观: 界面布局清晰,按钮排列合理,符合标准计算器的样式。
- 用户体验: 按钮点击有反馈(如颜色变化),计算过程和结果清晰可见。
- 代码规范: 代码结构清晰,注释充分,变量命名规范,遵循良好的编程实践。
三. 系统设计
技术选型:
- 编程语言: Java SE (Standard Edition)
- GUI库: Java Swing (javax.swing)
- 开发环境 (IDE): IntelliJ IDEA, Eclipse, 或 VS Code
类结构设计:

我们可以将整个计算器程序设计在一个主类中,Calculator.java。
Calculator类 (主类):- 职责: 创建主窗口,初始化所有UI组件(按钮、文本显示区),并管理事件监听器。
- 成员变量:
JFrame: 主窗口。JTextField: 用于显示输入和结果的文本框。JButton[]: 存储所有数字和功能按钮的数组。String: 存储当前输入的数字字符串。String: 存储第一个操作数。String: 存储当前选择的运算符。double: 存储内存中的值(用于内存功能)。boolean: 标记是否刚刚按下了等号,用于决定下一次输入是清屏还是继续在结果上输入。
界面布局设计:
使用 BorderLayout 作为主布局,将显示屏放在 NORTH 区域,将按钮面板放在 CENTER 区域,按钮面板内部使用 GridLayout 来整齐地排列按钮。
+-----------------------------------+
| [显示屏: JTextField] |
+-----------------------------------+
| [按钮面板: JPanel] |
| +---+---+---+---+ |
| | 7 | 8 | 9 | / | |
| +---+---+---+---+ |
| | 4 | 5 | 6 | * | |
| +---+---+---+---+ |
| | 1 | 2 | 3 | - | |
| +---+---+---+---+ |
| | C | 0 | . | + | |
| +---+---+---+---+ |
| | +/-| = | CE|<-| |
| +---+---+---+---+ |
+-----------------------------------+
四. 核心代码实现
下面是 Calculator.java 的核心代码,包含了详细的注释。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Calculator extends JFrame {
// --- UI Components ---
private JTextField displayField;
private JPanel buttonPanel;
// --- Calculator Logic Variables ---
private String currentInput = "";
private String firstOperand = "";
private String operator = "";
private boolean isResultDisplayed = false;
// --- Memory (Optional) ---
private double memory = 0.0;
public Calculator() {
// 1. Initialize the main window
setTitle("Java Calculator");
setSize(400, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout(10, 10));
setLocationRelativeTo(null); // Center the window
// 2. Create the display field
displayField = new JTextField("0");
displayField.setHorizontalAlignment(JTextField.RIGHT);
displayField.setEditable(false);
displayField.setFont(new Font("Arial", Font.BOLD, 24));
add(displayField, BorderLayout.NORTH);
// 3. Create the button panel
buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(5, 4, 10, 10)); // 5 rows, 4 columns
add(buttonPanel, BorderLayout.CENTER);
// 4. Define button labels
String[] buttonLabels = {
"7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"C", "0", ".", "+",
"+/-", "=", "CE", "<-"
};
// 5. Create and add buttons to the panel
for (String label : buttonLabels) {
JButton button = new JButton(label);
button.setFont(new Font("Arial", Font.BOLD, 18));
button.addActionListener(new ButtonClickListener());
buttonPanel.add(button);
}
}
// --- Inner class to handle button clicks ---
private class ButtonClickListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.matches("[0-9]")) {
// Number button clicked
handleNumberInput(command);
} else if (command.equals(".")) {
// Decimal point clicked
handleDecimalPoint();
} else if (command.equals("+/-")) {
// Sign change clicked
handleSignChange();
} else if (command.equals("C") || command.equals("CE")) {
// Clear button clicked
handleClear(command.equals("C"));
} else if (command.equals("<-")) {
// Backspace clicked
handleBackspace();
} else if (command.equals("=")) {
// Equals button clicked
handleEquals();
} else {
// Operator button clicked (+, -, *, /)
handleOperator(command);
}
}
private void handleNumberInput(String num) {
if (isResultDisplayed) {
currentInput = "";
isResultDisplayed = false;
}
if (currentInput.equals("0")) {
currentInput = num;
} else {
currentInput += num;
}
displayField.setText(currentInput);
}
private void handleDecimalPoint() {
if (isResultDisplayed) {
currentInput = "0";
isResultDisplayed = false;
}
if (!currentInput.contains(".")) {
currentInput += ".";
displayField.setText(currentInput);
}
}
private void handleSignChange() {
if (!currentInput.isEmpty() && !currentInput.equals("0")) {
if (currentInput.startsWith("-")) {
currentInput = currentInput.substring(1);
} else {
currentInput = "-" + currentInput;
}
displayField.setText(currentInput);
}
}
private void handleClear(boolean isFullClear) {
if (isFullClear) { // 'C' button
currentInput = "";
firstOperand = "";
operator = "";
displayField.setText("0");
} else { // 'CE' button
currentInput = "";
displayField.setText("0");
}
}
private void handleBackspace() {
if (currentInput.length() > 1) {
currentInput = currentInput.substring(0, currentInput.length() - 1);
} else {
currentInput = "0";
}
displayField.setText(currentInput);
}
private void handleOperator(String newOperator) {
if (!operator.isEmpty() && !isResultDisplayed) {
// If an operator is already set, calculate with the new one
handleEquals();
}
firstOperand = currentInput;
operator = newOperator;
isResultDisplayed = true;
}
private void handleEquals() {
if (operator.isEmpty() || firstOperand.isEmpty()) {
return;
}
double num1 = Double.parseDouble(firstOperand);
double num2 = Double.parseDouble(currentInput);
double result = 0;
try {
switch (operator) {
case "+":
result = num1 + num2;
break;
case "-":
result = num1 - num2;
break;
case "*":
result = num1 * num2;
break;
case "/":
if (num2 == 0) {
displayField.setText("Error");
currentInput = "";
firstOperand = "";
operator = "";
return;
}
result = num1 / num2;
break;
}
// Check for overflow
if (Double.isInfinite(result)) {
throw new ArithmeticException("Overflow");
}
currentInput = String.valueOf(result);
displayField.setText(currentInput);
isResultDisplayed = true;
operator = ""; // Reset operator for next calculation
} catch (ArithmeticException ex) {
displayField.setText("Error");
currentInput = "";
firstOperand = "";
operator = "";
}
}
}
// --- Main method to run the application ---
public static void main(String[] args) {
// Use SwingUtilities to ensure GUI creation is thread-safe
SwingUtilities.invokeLater(() -> {
Calculator calculator = new Calculator();
calculator.setVisible(true);
});
}
}
五. 测试方案
测试是确保程序质量的关键。
单元测试 (针对逻辑):
- 数字输入测试:
- 依次点击
1,2,3,显示屏应显示 "123"。 - 输入
0后再输入1,应显示 "1"。
- 依次点击
- 小数点测试:
- 输入
1, ,2,应显示 "1.2"。 - 连续点击两次小数点,第二次应无效。
- 输入
- 运算符测试:
- 输入
5, ,3, ,应显示 "8.0"。 - 输入
10, ,2, ,应显示 "5.0"。 - 输入
5, , ,3,应等价于5 * 3,最终结果应为 "15.0"。
- 输入
- 清除功能测试:
- 输入
123,点击CE,应显示 "0"。 - 输入
123,点击C,应显示 "0",且后续输入 ,5, 应报错(因为操作数丢失)。
- 输入
- 正负号测试:
- 输入
5,点击 ,应显示 "-5"。 - 再次点击 ,应显示 "5"。
- 输入
- 退格测试:
- 输入
1234,点击<-三次,应显示 "1"。
- 输入
- 错误处理测试:
- 输入
5, ,0, ,应显示 "Error"。 - 输入一个非常大的数(如
1E308),乘以2,应显示 "Error" 或 "Infinity"。
- 输入
集成测试 (针对流程):
- 连续计算:
10 + 5 = 15,* 2 = 30,- 10 = 20,结果应为 "20.0"。
- 混合运算:
100 * 2.5 / 10 - 5 =,结果应为 "20.0"。
六. 扩展与优化建议
如果你已经完成了基本功能,可以尝试以下扩展,让项目更出色:
- 科学计算器功能:
- 添加
sin,cos,tan,sqrt,x^y,log等按钮。 - 需要引入
java.lang.Math类。
- 添加
- 历史记录功能:
- 在计算器侧面或下方添加一个
JList来显示历史计算记录。 - 点击历史记录可以重新加载该表达式。
- 在计算器侧面或下方添加一个
- 主题切换:
- 提供深色模式和浅色模式切换。
- 通过修改UI组件的背景色和前景色实现。
- 键盘支持:
- 让计算器能够响应键盘输入(按键盘上的 键等同于点击 按钮)。
- 需要为
JFrame添加KeyListener。
- 代码重构:
- 将事件处理逻辑从
ButtonClickListener中分离出来,创建独立的NumberHandler,OperatorHandler等类,遵循单一职责原则。 - 使用
enum来管理运算符,使代码更清晰。
- 将事件处理逻辑从
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。