Java —— 计算器(前端+后台)
要求:
- 可以计算整型也可以计算浮点型
- 可以计算带括号的四则运算
- 当结果为整数时不带小数点
- 算出结果后若还有运算操作则结果就是第一个输入
- 可退格和清空
- 消除double运算的误差,若是无限循环小数则四舍五入保留两位小数
参考界面如下:
整型运算测试:
输入
输出
浮点型运算测试:
输入
输出
四舍五入保留两位小数测试:
运算结果:
前端代码如下:
Calc.java
package test1;
import java.awt.*;
import javax.swing.*;
public class Calc extends JFrame {
private void initFrame() {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
this.setSize(300, 400); // 窗口大小
this.setTitle("计算器"); // 窗口的名称
this.setLocationRelativeTo(null); // 窗口在屏幕的位置 默认中央
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE); // 设置单击窗口右上角后,程序的处理
this.setLayout(null); // 布局格式 默认
JTextField jtf = this.topSurfacee(); // 调用方法
this.bottomSurface(jtf);
this.setVisible(true); // 设置窗口是否可见 !放在前面窗口无内容
}
private JTextField topSurfacee() {
JPanel topPanel = new JPanel(); // 建立面板
topPanel.setBounds(-5, 10, 300, 50); // 窗口的在屏幕的位置
JTextField jtf = new JTextField(15); // 显示结果文本框长度15个字
jtf.setHorizontalAlignment(JTextField.RIGHT);// 设置文件右边往左边输出
jtf.setForeground(Color.red); // 设置组件的前景色
jtf.setFont(new Font("SansSerif", Font.PLAIN, 20)); // 字体样式 字体格式 字体大小
jtf.setEditable(false); // 设置选项不可用
jtf.setText("0"); // 文本框的文字设置为“确定”
topPanel.add(jtf); // 加入组件jtf
this.add(topPanel); // 加入组件topPanel
return jtf;
}
private void bottomSurface(JTextField jtf) {
JPanel bottomPanel = new JPanel(); // 建立面板
bottomPanel.setBounds(8, 80, 270, 250); // 面板的大小
bottomPanel.setLayout(new GridLayout(5, 4, 5, 4)); // 布局格式 5行4列 行距5 列距4
String[] buttonValue = { "1", "2", "3", "/", // 操作对应的字符串数组
"4", "5", "6", "*", "7", "8", "9", "-", "(", "0", ")", "+", "C", "←", ".", "=" };
calculation cl = new calculation(jtf); // 监听器的创建
for (int i = 0; i < buttonValue.length; i++) { // 遍历操作字符
JButton button = new JButton(buttonValue[i]); // 建立有文字的按钮
button.setBorder(BorderFactory.createRaisedBevelBorder());
button.setFont(new Font("楷体", 0, 25)); // 字体样式 字体格式 字体大小
button.setForeground(Color.black); // 设置组件的前景色
bottomPanel.add(button); // 添加组件
button.addActionListener(cl);
}
this.add(bottomPanel); // 添加组件
}
public static void main(String[] args) {
Calc calc = new Calc();
calc.initFrame();
}
}
后台代码如下:
calculation.java
package test1;
import java.util.*;
import java.math.*;
import javax.swing.JTextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class calculation implements ActionListener {
private JTextField jtf;
String content = "";
StringBuffer sb = new StringBuffer(); // 创建字符缓冲区域
public calculation(JTextField jtf) { // 构造方法
this.jtf = jtf;
}
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand(); // 返回与此动作相关的命令字符串
switch (command) {
// case同时写多个条件,数字的基本处理
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
case "0":
case ".":
case "+":
case "-":
case "*":
case "/":
case "%":
case "(":
case ")":
sb.append(command); // command添加到sb中
jtf.setText(sb.toString().trim()); // 在文本框添加字符串,并且去掉首尾空格
break;
// 清空输入框中内容
case "C":
// 清空StringBuffer
sb.setLength(0);
jtf.setText("0");
break;
// 退格
case "←":
if (sb.length() == 1) {
sb.deleteCharAt(sb.length() - 1);
jtf.setText("0");
} else {
sb.deleteCharAt(sb.length() - 1);
jtf.setText(sb.toString().trim());
}
break;
// 四则运算处理
case "=":
String content = jtf.getText(); // 得到字符串
List<String> expressionList = expressionToList(content.replace(" ", ""));
List<String> suffixList = parseToSuffixExpression(expressionList);
double calculateResult = calculate(suffixList);
if (isInteger(calculateResult)) {
jtf.setText(String.valueOf((int) calculateResult));
sb.setLength(0);
sb.append(String.valueOf((int) calculateResult));
} else{
jtf.setText(String.valueOf(calculateResult));
sb.setLength(0);
sb.append(String.valueOf(calculateResult));
}
break;
}
}
//具体运算实现
private static List<String> parseToSuffixExpression(List<String> expressionList) {
// 创建一个栈用于保存操作符
Stack<String> opStack = new Stack<>();
// 创建一个list用于保存后缀表达式
List<String> suffixList = new ArrayList<>();
for (String item : expressionList) {
// 得到数或操作符
if (isOperator(item)) {
// 是操作符 判断操作符栈是否为空
if (opStack.isEmpty() || "(".equals(opStack.peek()) || priority(item) > priority(opStack.peek())) {
// 为空或者栈顶元素为左括号或者当前操作符大于栈顶操作符直接压栈
opStack.push(item);
} else {
// 否则将栈中元素出栈如队,直到遇到大于当前操作符或者遇到左括号时
while (!opStack.isEmpty() && !"(".equals(opStack.peek())) {
if (priority(item) <= priority(opStack.peek())) {
suffixList.add(opStack.pop());
}
}
// 当前操作符压栈
opStack.push(item);
}
} else if (isNumber(item)) {
// 是数字则直接入队
suffixList.add(item);
} else if ("(".equals(item)) {
// 是左括号,压栈
opStack.push(item);
} else if (")".equals(item)) {
// 是右括号 ,将栈中元素弹出入队,直到遇到左括号,左括号出栈,但不入队
while (!opStack.isEmpty()) {
if ("(".equals(opStack.peek())) {
opStack.pop();
break;
} else {
suffixList.add(opStack.pop());
}
}
} else if (".".equals(item)) {
// System.out.print('a');
suffixList.add(item);
} else {
throw new RuntimeException("有非法字符!");
}
}
// 循环完毕,如果操作符栈中元素不为空,将栈中元素出栈入队
while (!opStack.isEmpty()) {
suffixList.add(opStack.pop());
}
return suffixList;
}
// 判断字符串是否为操作符
public static boolean isOperator(String op) {
return op.equals("+") || op.equals("-") || op.equals("*") || op.equals("/");
}
// 判断是否为整数或者浮点数
public static boolean isNumber(String num) {
return num.matches("^([0-9]{1,}[.][0-9]*)$") || num.matches("^([0-9]{1,})$");
}
// 判断是不是整数
public static boolean isInteger(Double num) {
if (num % 1 == 0)
return true;
else
return false;
}
// 获取操作符的优先级
public static int priority(String op) {
if (op.equals("*") || op.equals("/")) {
return 1;
} else if (op.equals("+") || op.equals("-")) {
return 0;
}
return -1;
}
// 为方便操作将表达式转为list
private static List<String> expressionToList(String expression) {
int index = 0;
List<String> list = new ArrayList<>();
do {
char ch = expression.charAt(index);
if (ch != 46 && (ch <= 47 || ch >= 58)) {
// 是操作符,直接添加至list中
index++;
list.add(ch + "");
} else {
// 是数字,判断多位数的情况
String str = "";
while (index < expression.length() && (expression.charAt(index) > 47 && expression.charAt(index) < 58
|| expression.charAt(index) == 46)) {
str += expression.charAt(index);
index++;
}
list.add(str);
}
} while (index < expression.length());
return list;
}
// 根据后缀表达式list计算结果
private static double calculate(List<String> list) {
Stack<Double> stack = new Stack<>();
for (int i = 0; i < list.size(); i++) {
String item = list.get(i);
if (item.matches("^([0-9]{1,}[.][0-9]*)$") || item.matches("^([0-9]{1,})$")) {
// 是数字(整型或者浮点型)
stack.push(Double.parseDouble(item));
} else {
// 是操作符,取出栈顶两个元素
double num2 = stack.pop();
double num1 = stack.pop();
double res = 0;
if (item.equals("+")) {
res = ArithUtil.add(num1, num2);
} else if (item.equals("-")) {
res = ArithUtil.sub(num1, num2);
} else if (item.equals("*")) {
res = ArithUtil.mul(num1, num2);
} else if (item.equals("/")) {
res = ArithUtil.div(num1, num2,2);
} else {
throw new RuntimeException("运算符错误!");
}
stack.push(res);
}
}
return stack.pop();
}
}
//double型精确计算
class ArithUtil{
private ArithUtil(){}
//精确的加法算法
public static double add(double d1,double d2){
BigDecimal b1=new BigDecimal(Double.toString(d1));
BigDecimal b2=new BigDecimal(Double.toString(d2));
return b1.add(b2).doubleValue();
}
//精确的减法算法
public static double sub(double d1,double d2){
BigDecimal b1=new BigDecimal(Double.toString(d1));
BigDecimal b2=new BigDecimal(Double.toString(d2));
return b1.subtract(b2).doubleValue();
}
//精确的乘法算法
public static double mul(double d1,double d2){
BigDecimal b1=new BigDecimal(Double.toString(d1));
BigDecimal b2=new BigDecimal(Double.toString(d2));
return b1.multiply(b2).doubleValue();
}
//相对精确的除法运算,当发生除不尽的情况时,精确到小数点以后2位
public static double div(double d1,double d2,int scale){
BigDecimal b1=new BigDecimal(Double.toString(d1));
BigDecimal b2=new BigDecimal(Double.toString(d2));
return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
}