简单加减乘除计算器的思路:
1、准备工作:
①定义一个类,继承于JFrame这个类,并且实现ActionListener这个接口。
②同时定义一个容器,用来存放按钮。
③定义一个字符串数组及按钮数组,两者的长度是相同的,这样就可以通过遍历这个字符串数组新建按钮。
④定义一个文本框,每一次点击按钮,文本框上出现相应的数字
2、设置窗口的主要参数,比如大小是多少等。
3、遍历字符串数组,从而将每一个按钮上有相应的数字,并且可以设置按钮的大小及按钮中的数字字体、大小。同时将按钮设置好之后,每一个按钮都要添加监听控件。
4、将按钮添加到容器中
5、遍历结束之后,就将这个容器添加到窗口中
点击按钮之后,处理过程的具体思路是:如果当前按钮对应的字符串是=号,那么就可以开始计算结果了,通过将文本框上的字符串转换成后缀表达式进行计算,如果不是=号,那么就将按钮对应的字符串添加到文本框中。
步骤:
步骤一、判断当前按钮的上对应的字符串是否是等于号,如果不是,那么将会有两种情况:
①如果当前按钮对应的字符串是基本运算符或者数字的话,那么直接将当前的按钮添加到文本框中,通过setText方法,设置文本框的内容,但是如果是直接setText()的话,那么之前文本框的内容就会发生改变,所以应该是先获取当前文本框的内容string,然后将string和当前按钮对应的字符串进行拼接,此时string才是完整的,才可以setText(),里面的参数是string。比如说,当前的文本框的内容是3+5,点击按钮之后,按钮对应的字符串是+号,那么如果直接将setText(当前按钮的字符串),那么最终显示再文本框中的字符串只有+,而没有了3+5。所以应该先调用方法getText()获取当前文本框上的内容,然后进行拼接,得到字符串string = 3+5+,此时再将string作为setText的参数才可以再文本框上显示 3+5+。
②如果cha当前按钮对应的字符串对应的不是基本运算符或者数字,而是“返回”这个字符串,那么就要删除一个字符,那么首先获取当前文本框中的字符串string,并且新建一个字符串str,然后进行遍历拼接,从而可以得到,注意因为“返回”只能清除一个字符,那么并没有完当前文本框的字符串string,只是遍历到string.length()-1,从而实现删除一个字符。
步骤二、如果当前按钮对应的字符串对应的是=号,那么开始计算,具体过程:
①获取当前文本框中的内容(是一个中缀表达式)
②将中缀表达式转换成为后缀表达式,具体过程:
-
从左到右开始遍历中缀表达式,
-
如果遇到的是数字,那么就考虑是否为多位数字或者小数,如果是,那么就要拼接,拼接完毕之后,将其压入到栈1中
-
如果遇到的是基本的运算符,那么就要将分几种情况考虑:
1)如果栈2为空或者栈2的栈顶是左括号或者当前的符号就是左括号,那么直接将其压入栈2
2)如果遇到的是右括号,那么就通过循环遍历栈2,从栈2中跳出符号,然后将其这些符号压入到栈1中, 直到栈2的栈顶符号为左括号,结束遍历,最后还要将左括号从栈2中跳出,从而消除左括号
3)如果符号都不符合上面的几种情况,那么还要分几种情况:
①如果当前的符号优先级大于栈顶符号的优先级,那么直接入栈,
②当前符号的优先级小于或者等于栈顶符号的优先级,那么就从栈2中跳出,并将这个符号压入到栈1,最后才可以将当前符号压入到栈2
4)压入完毕之后,将栈1中的符号全部压入到栈2,否则如果将栈2中的符号压入到栈1中,得到的是后缀表达式的倒序
5)定义一个字符串列表,用于存放后缀表达式
步骤三、获得后缀表达式之后,遍历字符串列表,计算运算结果,这一步也需要配合一个栈来实现,如果遇到的是一个数字,那么就将其压入栈中,否则遇到的是基本运算符,那么就从栈中跳出两个数字,注意首先跳出栈的是num1,然后跳出栈的是num2,那么进行减法运算、除法运算必须是num2 - num1,num2 / num1,否则运算结果是不正确的。比如原来的中缀表达式为2+3÷5,那么字符串列表为[2,3,5,÷,+],那么遇到÷号之后,首先跳出栈的数字是num1 = 5,然后num2为3,那么这里运算应该是num2 / num1,同时在计算的时候注意运算规则,比如除数不可以为0等情况。
代码:
/**
* 制作一个简单计算器GUI
* 1、定义一个类,从而使得其继承于窗口,并且实现于监听接口
* 2、对窗口进行设置相应的参数
* 3、向窗口中添加按钮,从而实现基本的运算
* 4、向窗口中添加文本框,这样在点击按钮的时候就会出现相应的数字
* 5、将向按钮中添加监听控件,从而点击按钮的时候,将对应的数字添加到文本框中,通过调用文本框的setText方法即可
* 6、点击到等号的时候,将文本框中的字符串转换成后缀表达式,进行相应的运算
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class Calculator extends JFrame implements ActionListener {
public JPanel panel = new JPanel();
public String[] string = {"(",")","^","%","7","8","9","x","4","5","6","÷","1","2","3","-","0",".","=","+","返回"};
public JButton[] b = new JButton[string.length];
public JTextField text = new JTextField();//设置文本框
public Stack<String> stack1 = new Stack<>();
public Stack<String> stack2 = new Stack<>();
public void setframe(){
Dimension dimension = new Dimension(738,90);//设置文本框的大小
text.setPreferredSize(dimension);
panel.add(text,BorderLayout.NORTH);
for(int i = 0; i<string.length; i++){
Dimension dimension1 = new Dimension(180,80);
b[i] = new JButton(string[i]);//初始化按钮
b[i].setFont(new Font("宋体",Font.BOLD,36));//设置按钮中的字体
b[i].setPreferredSize(dimension1);//设置按钮的大小
b[i].addActionListener(this);//将每一个容器都添加监听控件
panel.add(b[i]);//将按钮添加到容器中
}
this.add(panel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//可以退出
this.setVisible(true);
this.setBounds(100,100,800,650);
this.setTitle("my calculator");
}
public static void main(String[] args){
new Calculator().setframe();
}
@Override
public void actionPerformed(ActionEvent e) {
if(!e.getActionCommand().equals("=")) {
//没有等于=号的时候,考虑两种情况,一是当前按钮不是返回键,而是当前按钮是返回键
if(!e.getActionCommand().equals("返回")) {
//如果当前按钮不是返回键,那么就将当前按钮上的字符添加到文本框中
text.setText(text.getText() + e.getActionCommand());//获取文本框中的字符
text.setFont(new Font("宋体", Font.BOLD, 36));//设置文本框中的字体
}else{
//清除文本框的最后一个字符,方法一:通过遍历即可,此时不需要考虑文本框是否为空
// String str = "";
// String str2 = text.getText();
// for(int i = 0; i < str2.length() - 1; i++){
// str += str2.substring(i,i+1);
// }
// text.setText(str);
String str1 = text.getText();
StringBuffer str = new StringBuffer(str1);
if(str1.length() != 0) {
str.deleteCharAt(str1.length() - 1);
String string = str.toString();
text.setText(string);
}else{
//如果文本框的内容为空了,那么就不可以在清除了,此时就将文本框设置成空字符串即可,如果没有这一步的话,在文本框为空的时候,点击返回,就会报错,而方法一就不会
text.setText("");
}
}
}else if(e.getActionCommand().equals("=")){
//如果是等号,那么就将从文本框中获取字符串,然后进行中缀表达式转换成后缀表达式
String str = text.getText();//从文本框中获取字符串
//调用方法,从而将中缀表达式转换成为后缀表达式
List<String> list = getSufExpression(str);//获得后缀表达式
//计算后缀表达式的结果
try{
String result = getResult(list);
text.setText(result);
}catch(Exception e1){
text.setText(e1.getMessage());
}
}
}
/**
* 调用方法,从而获得后缀表达式
* 思路:
* 1、定义两个栈,栈1用于储存数字,栈2用于储存基本的运算符
* 2、从左到右开始遍历字符串,如果遇到的是数字,那么就考虑是否为多位数字或者小数,如果是,那么就要拼接
* 3、如果遇到的是基本的运算符,那么就要将分几种情况考虑:
* 1)如果栈2为空或者栈2的栈顶是左括号或者当前的符号就是左括号,那么直接将其压入栈2
* 2)如果遇到的是右括号,那么就从栈2中跳出符号,然后将其这些符号压入到栈1中,直到遇到的栈顶符号为左括号,最后还要将左括号
* 从栈2中跳出,从而消除左括号
* 3)如果符号是基本运算符号,那么还要分几种情况:
* ①如果当前的符号优先级大于栈顶符号的优先级,那么直接入栈,
* ②当前符号的优先级小于或者等于栈顶符号的优先级,那么就从栈2中跳出,并将这个符号压入到栈1,最后才可以将当前符号压入到栈2
* 4、压入完毕之后,将栈1中的符号全部压入到栈2,否则如果将栈2中的符号压入到栈1中,得到的是后缀表达式的倒序
* 5、定义一个字符串列表,用于存放后缀表达式
* @param string
* @return
*/
public List<String> getSufExpression(String string){
List<String> list = new ArrayList<String>();
String keepNum = "";//定义一个空字符串,用于多位数字、小数的拼接
for(int i = 0; i<string.length(); i++){
String str = string.substring(i,i+1);
if(isOper(str)){
//如果是基本运算符
if(stack2.empty() || stack2.peek().equals("(") || str.equals("(")){
//如果当前符号是左括号或者栈2为空或者栈顶符号是左括号,那么就直接将符号入栈
stack2.push(str);
}else if(str.equals(")")){
//如果当前符号是右括号,那么从栈2中不断跳出符号,然后将这些从栈2跳出的符号压入到栈1中,直到栈顶符号位左括号,然后将左括号从栈2中跳出
while(!stack2.peek().equals("(")){
String item = stack2.pop();
stack1.push(item);
}
stack2.pop();//消除左括号
}else{
//如果都不符合上面的情况,那么就开始比较优先级
if(getPriority(str) > getPriority(stack2.peek())){
//如果当前符号的优先级大于栈2栈顶符号的优先级,那么就将当前符号直接入栈
stack2.push(str);
}else{
//当前符号的优先级小于或者等于栈顶符号的优先级,那么就从栈顶中跳出符号,并将这个跳出的符号压入到栈1中,然后再将当前符号入栈
String item2 = stack2.pop();
stack1.push(item2);
stack2.push(str);
}
}
}else{
//如果当前符号不是基本的运算符,那么就将其压入到栈1,同时还要考虑多位数字、小数的情况
keepNum += str;
int j ;
for(j = i+1; j < string.length(); j++){
String item3 = string.substring(j,j+1);
if(isOper(item3)){
//如果当前符号是基本的运算符,那么就停止数字的拼接
break;
}
keepNum += item3;
}
stack1.push(keepNum);
keepNum = "";//重置keepNum
i = j - 1;
}
}
//遍历完毕之后,将栈1中的所有符号都压入到栈2中,从而遍历栈2的时候得到的是后缀表达式
while(!stack1.empty()){
String item = stack1.pop();
stack2.push(item);
}
//遍历栈2元素,然后将其添加到字符串列表中储存
while(!stack2.empty()){
String item = stack2.pop();
list.add(item);
}
return list;//返回字符串列表,从而得到的是后缀表达式
}
/**
* 计算后缀表达式的结果
* 思路:通过配合栈来使用
* 1、定义一个栈
* 2、如果遇到的是数字,那么就将其压入栈中
* 3、遇到的是基本运算符,那么就从栈中跳出两个数字,并进行相应的运算
* 4、遍历结束之后,栈中剩余的那个数字就是运算结果
* @param list 后缀表达式
* @return
*/
public String getResult(List<String> list){
for(int i = 0; i<list.size() ; i++){
String item = list.get(i);
if(isOper(item)){
//如果当前的符号是基本运算符,那么就从栈中跳出两个数字,然后进行相应的运算
double num1 = Double.parseDouble(stack1.pop());
double num2 = Double.parseDouble(stack1.pop());//由于栈是先进后出的,那么进行运算的时候,应该是num2 - num1等
//进行运算之后,将相应的结果压入栈中
switch(item){
case "+":
stack1.push(String.valueOf(num1 + num2));
break;
case "-":
stack1.push(String.valueOf(num2 - num1));
break;
case "÷":
if(num1 == 0)
throw new RuntimeException("错误,除数不可以为0");
stack1.push(String.valueOf(num2 / num1));
break;
case "x":
stack1.push(String.valueOf(num2 * num1));
break;
case "%":
if(num1 == 0)
throw new RuntimeException("错误,取模时除数不可以为0");
stack1.push(String.valueOf(num2 % num1));
break;
case "^":
double value = Math.pow(num2,num1);
stack1.push(String.valueOf(value));
break;
default:
break;
}
}else{
//如果当前符号不是基本运算符,那么就将其压入栈中
stack1.push(item);
}
}
return stack1.pop();//返回运算结果
}
/**
* 判断是否为基本运算符
* @param string
* @return
*/
public boolean isOper(String string){
return (string.equals("^")||string.equals("+") ||string.equals("-")||string.equals("x") ||string.equals("÷") ||string.equals("=")||string.equals("(") || string.equals(")") || string.equals("%"));
}
/**
* 比较基本运算符的优先级
* @param string
* @return
*/
public int getPriority(String string){
if(string.equals("^")){
return 4;
}if(string.equals("%")){
return 3;
}else if(string.equals("÷") || string.equals("x")){
return 2;
}else if(string.equals("+") || string.equals("-")){
return 1;
}
return 0;//都不符合,那么就返回0
}
}
现在的代码不足之处就是功能有些少,同时还有一种情况是不可以处理的,比如直接输入-10%2,或者-10+2,就是最开始文本框的首个字符是基本运算符得没有办法处理,如果以后想到了,会修改的,请大家见谅。同时如果哪里有不足之处,请大家指正。