前言
第一次用Java写,好多东西都是边写边学的,所以写了很多备注(其实也不多,主要还是因为要发CSDN之类的所以后面加了一些)。没写过这种程序,估计有不规范的地方,可能还有bug之类的。
虽然我还是Java小白,但是IntelliJ好用啊哈哈,智能补全就不说了,它甚至还能帮你优化代码,真的,我哭死!!!
震惊老师的开发速度哈哈
背景
写一个支持级联调用的计算器类,能完成简单的四则运算,调用示例:
var result=new Calc(100).add(10).subtract(5).multiply(10).getResult();
上面的完成的是100+10-5*10的计算,不考虑优先级,纯从左到右顺序计算。
要求:
(1)计算器类必须是只读类型
(2)级联调用方法链的长度不限
(3)最后提供一个“终端方法”,结束整个调用,并给出计算结果。
正好学了一下JavaFX的基础就用一下。其他的都还好,计算那部分代码其实和数据结构的国庆作业有道题很像,只是把C语言实现变成Java实现而已,用Java还简单一点。主要问题还是写的太急,功能没考虑全就写了,导致后面改代码费时间(记性差,忘了哪部分是哪部分的功能了,主要第一次写的时候没怎么注释,就给方法注释了一下)。
要求计算器类要是只读类,但是我不太懂这个(当时正好没听课——在研究别人的软件),等之后再看看。
var result=new Calc(100).add(10).subtract(5).multiply(10).getResult();这段要求也好迷,不过我觉得我这样写也实现了连续计算和调用方法不限,不过嘛,要是字符超过文本框就看不见了,该写一个文本框内字体大小自动调节的,今天太晚了不想写,明天早八游泳,睡觉!!!
package com.example.hellofx;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class Calculator extends Application {
private TextField inputField;
@Override
public void start(Stage primaryStage) {
// 设置主舞台的标题
primaryStage.setTitle("Calculator");
// 创建网格布局容器
GridPane gridPane = createGridPane();
// 创建场景,将网格布局容器作为根节点,设置场景的宽度为 300 像素,高度为 400 像素
Scene scene = new Scene(gridPane, 320, 502);
// 将场景设置到主舞台
primaryStage.setScene(scene);
// 创建输入文本框,并将其添加到网格布局容器的第一行
inputField = createInputField();
gridPane.add(inputField, 0, 0, 4, 1);
// 添加按钮到网格布局容器的指定位置
addButtonToGrid(gridPane, "AC", 0, 1);
addButtonToGrid(gridPane, "DEL", 1, 1);
addButtonToGrid(gridPane, "(", 2, 1);
addButtonToGrid(gridPane, ")", 3, 1);
addButtonToGrid(gridPane, "7", 0, 2);
addButtonToGrid(gridPane, "8", 1, 2);
addButtonToGrid(gridPane, "9", 2, 2);
addButtonToGrid(gridPane, "/", 3, 2);
addButtonToGrid(gridPane, "4", 0, 3);
addButtonToGrid(gridPane, "5", 1, 3);
addButtonToGrid(gridPane, "6", 2, 3);
addButtonToGrid(gridPane, "*", 3, 3);
addButtonToGrid(gridPane, "1", 0, 4);
addButtonToGrid(gridPane, "2", 1, 4);
addButtonToGrid(gridPane, "3", 2, 4);
addButtonToGrid(gridPane, "-", 3, 4);
addButtonToGrid(gridPane, ".", 0, 5);
addButtonToGrid(gridPane, "0", 1, 5);
addButtonToGrid(gridPane, "00", 2, 5);
addButtonToGrid(gridPane, "+", 3, 5);
addButtonToGrid(gridPane, "=", 3, 6, 2);
primaryStage.show();
}
private GridPane createGridPane() {
// 创建一个网格布局容器对象
GridPane gridPane = new GridPane();
// 设置网格布局容器中列之间的水平间距为 6 像素
gridPane.setHgap(6);
// 设置网格布局容器中行之间的垂直间距为 9 像素
gridPane.setVgap(9);
// 设置网格布局容器的内边距为 10 像素
gridPane.setPadding(new Insets(10));
// 返回创建的网格布局容器对象
return gridPane;
}
private TextField createInputField() {
// 创建一个文本框对象
TextField textField = new TextField();
// 其实这块没懂怎么调的位置,就是搜了一下教程套了调位置的代码,试了几次数据
textField.setStyle("-fx-alignment: bottom-left;");
textField.setStyle("-fx-padding: 0 0 -30 0;");
// 将文本框设置为不可编辑
textField.setEditable(false);
// 设置文本框的首选高度为 125 像素
textField.setPrefHeight(125);
// 设置字体大小为 21
textField.setFont(Font.font(21));
// 返回创建的文本框对象
return textField;
}
private void addButtonToGrid(GridPane gridPane, String text, int col, int row) {
addButtonToGrid(gridPane, text, col, row, 1);
}
//接受一个额外的 colSpan 参数,用于指定按钮在水平方向上跨越的列数,控制按钮在网格布局中的水平空间占用。
private void addButtonToGrid(GridPane gridPane, String text, int col, int row, int colSpan) {
// 创建按钮并设置按钮文本
Button button = new Button(text);
// 设置按钮点击事件的处理程序为 handleButtonAction 方法,传递按钮文本作为参数
button.setOnAction(e -> handleButtonAction(text));
// 设置按钮的首选大小
button.setPrefSize(70, 50);
// 设置字体大小为 16
button.setFont(Font.font(16));
// 将按钮添加到网格布局中的指定位置
gridPane.add(button, col, row, colSpan, 1);
}
private void handleButtonAction(String text) {
switch (text) {
case "=" -> {
// 点击 "=" 按钮时,计算表达式的结果并显示在输入框中
// 折磨我半天的整数显示问题,我还一直在evaluateExpression里面改,后面忘了这里转化为String了
String expression = inputField.getText();
double result = evaluateExpression(expression);
if (result % 1 == 0) {
int intResult = (int) result;
inputField.setText(String.valueOf(intResult));
} else {
inputField.setText(String.valueOf(result));
}
}
case "AC" ->
// 点击 "AC" 按钮时,清空输入框
inputField.clear();
case "DEL" -> {
// 点击 "Delete" 按钮时,从输入框中删除一个字符
String currentText = inputField.getText();
if (!currentText.isEmpty()) {
inputField.setText(currentText.substring(0, currentText.length() - 1));
}
}
default ->
// 将点击的按钮文本追加到输入框中
inputField.appendText(text);
}
}
private double evaluateExpression(String expression) {
// 去除表达式中的空格
expression = expression.replaceAll("\\s+", "");
// 初始化运算符栈和操作数栈
Stack<Character> operatorStack = new Stack<>();
Stack<Double> operandStack = new Stack<>();
// 定义运算符优先级
Map<Character, Integer> precedence = new HashMap<>();
precedence.put('+', 1);
precedence.put('-', 1);
precedence.put('*', 2);
precedence.put('/', 2);
int i = 0;
while (i < expression.length()) {
char ch = expression.charAt(i);
if (Character.isDigit(ch) || ch == '.') {
// 处理数字
StringBuilder sb = new StringBuilder();
i = getI(expression, operandStack, i, sb);
} else if (ch == '(') {
// 处理左括号
operatorStack.push(ch);
i++;
} else if (ch == ')') {
// 处理右括号
while (!operatorStack.isEmpty() && operatorStack.peek() != '(') {
applyOperator(operatorStack.pop(), operandStack, operatorStack);
}
operatorStack.pop(); // 弹出左括号
i++;
} else if (isOperator(ch)) {
// 处理运算符
if (ch == '-' && (i == 0 || expression.charAt(i - 1) == '(')) {
// 当运算符是负号且位于表达式开头或左括号后时,将负数符号与操作数一起推入操作数栈
i++;
StringBuilder sb = new StringBuilder("-");
i = getI(expression, operandStack, i, sb);
} else {
while (!operatorStack.isEmpty() && operatorStack.peek() != '(' && precedence.get(ch) <= precedence.get(operatorStack.peek())) {
//运算符不是负号且满足条件.弹出栈顶运算符并将其应用于操作数栈中的操作数
applyOperator(operatorStack.pop(), operandStack, operatorStack);
}
// 将当前运算符压入运算符栈
operatorStack.push(ch);
i++;
}
} else {
// 忽略其他字符(例如空格)
i++;
}
}
// 处理剩余的运算符
while (!operatorStack.isEmpty()) {
applyOperator(operatorStack.pop(), operandStack, operatorStack);
}
// 返回最终结果
if (!operandStack.isEmpty()) {
return operandStack.pop();
} else {
throw new IllegalArgumentException("Invalid expression");
}
}
private int getI(String expression, Stack<Double> operandStack, int i, StringBuilder sb) {
// 从当前位置开始,连续获取数字字符或小数点字符,将其添加到 StringBuilder 中,直到遇到非数字非小数点的字符
while (i < expression.length() && (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) {
sb.append(expression.charAt(i));
i++;
}
// 将 StringBuilder 中的字符串解析为 double 类型的操作数
double operand = Double.parseDouble(sb.toString());
// 将操作数压入操作数栈中
operandStack.push(operand);
// 返回更新后的位置索引
return i;
}
private boolean isOperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
private void applyOperator(char operator, Stack<Double> operandStack, Stack<Character> operatorStack) {
if (operandStack.size() < 2) {
// 操作数栈中的操作数数量不足,无法进行运算
throw new IllegalArgumentException("Invalid expression");
}
double operand2 = operandStack.pop();
double operand1 = operandStack.pop();
double result = switch (operator) {
case '+' -> operand1 + operand2;
case '-' -> operand1 - operand2;
case '*' -> operand1 * operand2;
case '/' -> {
if (operand2 == 0) {
// 除数为零,异常
throw new ArithmeticException("Division by zero");
}
yield operand1 / operand2;
}
default -> 0; // 默认情况下,返回0
};
// 将运算结果压入操作数栈
operandStack.push(result);
// 处理负数结果的情况
if (!operandStack.isEmpty() && operandStack.peek() < 0) {
if (!operatorStack.isEmpty()) {
char nextOperator = operatorStack.peek();
if (nextOperator == '+' || nextOperator == '-') {
// 当下一个运算符是加号或减号时,将负数结果转换为负数
double negativeResult = operandStack.pop();
operandStack.push(-negativeResult);
}
}
}
}
public static void main(String[] args) {
launch(args);
}
}