一、项目简介
功能描述:
一个简单的计算机程序,具有图形用户界面,在界面上可以点击相应按键执行加减乘除操作,也可在键盘上进行输入输出。可以进行简单的整数四则运算。
二、功能架构图或主要功能流程图
三、任务简述
序号 | 完成功能与任务 | 描述 |
1 | 窗口界面设计 | 使用了Java中的swing库来实现窗口界面的设计,使用了awt库中的一些类。如Jframe、JPanel,提供了一些基本的窗口和组件类 |
2 | 接收键盘和图形界面的输入 | 使用了KeyListener、ActionListener等技术来监听键盘、按钮的输入 |
3 | 文本框内容显示 | 通过JTextField组件实现的。 |
4 | 历史记录存储 | 使用了Java的文件操作技术,通过创建文件输出流和输出流写入器,将计算表达式和结果写入文件 |
5 | 移除空格 | expression.replaceAll("\\s+", "") 用于移除输入表达式中的所有空格,确保空格不会影响表达式的解析。 |
6 | 拆分表达式 | expression.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)") 用正则表达式将输入的数学表达式拆分为数字和运算符 |
7 | 处理数字和运算符 | 通过遍历拆分后的元素(tokens),使用 isNumber 方法判断是否为数字。使用 isOperator 方法判断是否为运算符。 |
8 | 计算表达式 | 遇到运算符时,如果当前运算符的优先级低于或等于栈顶运算符的优先级,则使用 applyOperator 方法计算栈顶两个数字和一个运算符的结果,并将结果推回 numbers 栈。然后将当前运算符推入 operators 栈 |
9 | 计算剩余运算符 | 当所有的 tokens 都被处理完后,如果 operators 栈中还有运算符,继续使用 applyOperator 方法完成剩余的计算。。 |
10 | 判断数字和运算符 | 尝试将 token 转换为 double 类型来判断是否是数字。首先检查 token 的长度是否为 1,以确保只有一个字符。然后,使用 "+-*/".contains(token) 来判断 token 是否包含在运算符字符串中。如果是有效的运算符,则返回 true |
11 | 给定相应运算的功能 | ExpressionEvaluator 类中的私有方法 applyOperator,用于根据给定的运算符 operator 对两个操作数 operand1 和 operand2 进行相应的运算,并返回计算结果。 |
四、功能详解
1. *窗口界面设计
创建一个计算器界面,并初始化计算器的各个组件和属性。首先,代码设置计算器的标题为"计算器",并设置当关闭计算器窗口时退出程序。
然后,使用BorderLayout布局管理器设置计算器的布局。在计算器的北部(上方),创建一个JTextField对象作为显示器(displayField)。设置显示器为只读(不可编辑),并将文本的对齐方式设置为右对齐。将显示器添加到容器中的北部区域。
最后,创建一个JPanel对象作为按钮面板(buttonPanel),使用GridLayout布局管理器将按钮排列成5行4列的网格。定义一个字符串数组(buttonLabels),包含计算器的按钮标签。使用循环遍历按钮标签数组,创建JButton对象,并为每个按钮添加ActionListener监听器。将按钮添加到按钮面板中。
public class Calculator extends JFrame implements ActionListener, KeyListener {
private JTextField displayField;
private StringBuilder inputBuffer;
public Calculator() {
setTitle("计算器");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
displayField = new JTextField();
displayField.setEditable(false);
displayField.setHorizontalAlignment(JTextField.RIGHT);
add(displayField, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel(new GridLayout(5,4));
String[] buttonLabels = {
"AC","→",""," ",
"7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+"
};
for (String label : buttonLabels) {
JButton button = new JButton(label);
button.addActionListener(this);
buttonPanel.add(button);
}
add(buttonPanel, BorderLayout.CENTER);
inputBuffer = new StringBuilder();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
2. *接收键盘和图形界面的输入
处理计算器界面上的按钮点击事件和键盘输入事件。
在actionPerformed方法中,根据不同的按钮命令(command),执行相应的操作。如果命令是"AC",表示清空输入,将输入缓冲区(inputBuffer)清空并将显示器(displayField)的文本设置为空。如果命令是"=",表示计算结果,调用calculateResult方法进行计算。如果命令是"→",表示删除最后一个字符,如果输入缓冲区不为空,删除最后一个字符并更新显示器的文本。否则,将命令追加到输入缓冲区并更新显示器的文本。
在keyTyped方法中,根据键盘输入的字符判断是否为数字、小数点或运算符,如果是,则将字符追加到输入缓冲区并更新显示器的文本。
在keyPressed方法中,根据键盘按键的keyCode判断是否为回车键或退格键。如果是回车键,调用calculateResult方法进行计算。如果是退格键,如果输入缓冲区不为空,删除最后一个字符并更新显示器的文本。
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("AC")) {
inputBuffer.setLength(0);
displayField.setText("");
} else if (command.equals("=")) {
calculateResult();
} else if (command.equals("→")) {
if (inputBuffer.length() > 0) {
inputBuffer.deleteCharAt(inputBuffer.length() - 1);
displayField.setText(inputBuffer.toString());
}
} else {
inputBuffer.append(command);
displayField.setText(inputBuffer.toString());
}
}
@Override
public void keyTyped(KeyEvent e) {
char keyChar = e.getKeyChar();
if (Character.isDigit(keyChar) || keyChar == '.' || keyChar == '+' || keyChar == '-' || keyChar == '*' || keyChar == '/' || keyChar == '=') {
inputBuffer.append(keyChar);
displayField.setText(inputBuffer.toString());
}
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
calculateResult();
} else if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
if (inputBuffer.length() > 0) {
inputBuffer.deleteCharAt(inputBuffer.length() - 1);
displayField.setText(inputBuffer.toString());
}
}
}
@Override
public void keyReleased(KeyEvent e) {
}
3. *文本框内容显示
初始化计算器的界面和输入缓冲区。使用BorderLayout布局管理器设置计算器的布局。在计算器的北部(上方),创建一个JTextField对象作为显示器(displayField)。设置显示器为只读(不可编辑),并将文本的对齐方式设置为右对齐。将显示器添加到容器中的北部区域。创建一个StringBuilder对象,并将其赋值给inputBuffer变量。inputBuffer用于存储用户输入的表达式。
private JTextField displayField;
private StringBuilder inputBuffer;
public Calculator() {
setTitle("计算器");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
displayField = new JTextField();
displayField.setEditable(false);
displayField.setHorizontalAlignment(JTextField.RIGHT);
add(displayField, BorderLayout.NORTH);
add(buttonPanel, BorderLayout.CENTER);//将名为buttonPanel的组件添加到容器中,并使用
BorderLayout布局管理器将其放置在容器的中心区域。
inputBuffer = new StringBuilder();//创建一个StringBuilder对象,并将其赋值给
inputBuffer变量,用于存储用户输入的表达式。
4. 历史记录存储
计算输入的表达式,并将结果显示在界面上。创建一个CalculationSaver对象(计算结果保存器),并调用saveCalculation方法,将表达式和结果保存到指定的文件路径中。如果在计算过程中出现异常,代码将显示器上的文本设置为"错误"。
private void calculateResult() {//计算输入的表达式,并将结果显示在界面上。
try {
String expression = inputBuffer.toString();
ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator();
double result = expressionEvaluator.evaluateExpression(expression);
displayField.setText(String.valueOf(result));// 将计算结果转换为字符串类型,并设置到显示器(displayField)上
CalculationSaver calculationSaver = new CalculationSaver("文件路径");
// 调用CalculationSaver对象的saveCalculation方法,传入表达式和结果,将其保存到文件中
calculationSaver.saveCalculation(expression, result);
} catch (Exception e) {// 如果在计算过程中出现异常,将显示器上的文本设置为"错误"
displayField.setText("错误");
} finally { // 最后,清空输入缓冲区(inputBuffer)
inputBuffer.setLength(0);
}
}
5、计算
根据给定的运算符和两个操作数,应用运算符进行计算并返回结果。
private double applyOperator(char operator, double operand2, double operand1) {
// 应用运算符计算结果
switch (operator) {
case '+':
return operand1 + operand2;
case '-':
return operand1 - operand2;
case '*':
return operand1 * operand2;
case '/':
return operand1 / operand2;
default:
throw new IllegalArgumentException("非法运算符: " + operator);
}
}
6、表达式求值器
接受一个数学表达式作为输入,并计算出表达式的结果。
expression.replaceAll("\\s+", "") 用于移除输入表达式中的所有空格,确保空格不会影响表达式的解析。
expression.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)") 用正则表达式将输入的数学表达式拆分为数字和运算符
public double evaluateExpression(String expression) {
// 移除表达式中的空格
expression = expression.replaceAll("\\s+", "");
// 使用正则表达式将表达式拆分为数字和运算符
String[] tokens = expression.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)");
for (String token : tokens) {
if (isNumber(token)) {
// 如果是数字,则将其转换为double类型并入栈
numbers.push(Double.parseDouble(token));
} else if (isOperator(token)) {
// 如果是运算符,则根据优先级进行计算
char operator = token.charAt(0);
while (!operators.isEmpty() && hasHigherPrecedence(operators.peek(), operator)) {
double result = applyOperator(operators.pop(), numbers.pop(), numbers.pop());
numbers.push(result);
}
operators.push(operator);
}
}
// 计算剩余的运算符
while (!operators.isEmpty()) {
double result = applyOperator(operators.pop(), numbers.pop(), numbers.pop());
numbers.push(result);
}
// 返回最终结果
return numbers.pop();
}
7、判断优先级
判断token是否为数字、运算符以及判断运算符的优先级。
isNumber 方法用于判断 token 是否是一个有效的数字。它通过尝试将 token 转换为 double 类型来判断是否是数字。如果转换成功,则返回 true,否则捕获 NumberFormatException 异常并返回 false。
isOperator 方法用于判断 token 是否是一个有效的运算符。它首先检查 token 的长度是否为 1,以确保只有一个字符。然后,使用 "+-*/".contains(token) 来判断 token 是否包含在运算符字符串中。如果是有效的运算符,则返回 true,否则返回 false。
private boolean isNumber(String token) {
// 判断是否是数字
try {
Double.parseDouble(token);
return true;
} catch (NumberFormatException e) {
return false;
}
}
private boolean isOperator(String token) {
// 判断是否是运算符
return token.length() == 1 && "+-*/".contains(token);
}
private boolean hasHigherPrecedence(char operator1, char operator2) {
// 判断运算符的优先级
return (operator1 == '*' || operator1 == '/') && (operator2 == '+' || operator2 == '-');
}
五、总结
本次设计运用了Java编程语言和相关的GUI库(如javax.swing和java.awt)来实现计算器的界面和交互功能。它使用了面向对象的编程思想,将计算器的各个组件和功能封装成类,并通过继承和实现接口的方式进行扩展和重写。还使用了面向对象编程、布局管理器、事件处理、数据结构、正则表达式、异常处理和文件操作等技术。实现了一个简单的计算器程序,可以进行基本的数学运算,并将计算结果保存到文件中。
六、展望
计算器不支持一些复杂的计算。未来可以增加功能使得计算器可以进行一些复杂的计算,比如三角函数,指数函数等。也可以增加打开多窗口来查看历史记录,复制粘贴等功能。