先放上代码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EmptyStackException;
import java.util.Stack;
public class Main extends JFrame implements ActionListener{
/**@*************成员变量的声明与实例化**************/
//声明按钮数组
JButton bt[]=new JButton[18];
//面板
JPanel JP1=new JPanel();
JPanel JP2=new JPanel();
JPanel JP3=new JPanel();
JPanel JP4=new JPanel();
JPanel JP5=new JPanel();
JPanel JP6=new JPanel();
JPanel JP7=new JPanel();
JPanel JP8=new JPanel();
//标签和文本框
JLabel JL1=new JLabel("输入框:");
JLabel JL2=new JLabel("输出框:");
JTextArea JA1=new JTextArea(1/2,12);
JTextField JT2=new JTextField(12);
/**@**************成员变量的声明与实例化************/
/**@构造方法*/
public Main(){
initialize();//初始化容器方法
//窗体设置
this.setTitle("计算器");
this.setBounds(600,200,320,500);
this.setSize(500,500);
this.setVisible(true);
this.setLayout(new GridLayout(8,1));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**@初始化方法*/
public void initialize() {
//初始化与添加按钮
String buttonName[] = {"清空", "退格", "%", "/", "*","+","-", "=", "7","8","9", "4","5" ,"6", "1", "2", "3", "0"};
for (int i = 0; i < buttonName.length; i++) {
bt[i] = new JButton(buttonName[i]);
bt[i].setForeground(Color.BLUE);//设置按钮背景颜色
bt[i].setFont(new Font("行书", Font.PLAIN, 40));//设置文字样式
bt[i].addActionListener(this);//为此时的按钮添加监听器
//添加按钮到面板上
if (i >= 0 && i <= 1) {
JP3.add(bt[i]);
} else if (i >= 4 && i <= 7) {
JP4.add(bt[i]);
} else if (i >= 8 && i <= 10) {
JP5.add(bt[i]);
} else if (i >= 11 && i <= 13) {
JP6.add(bt[i]);
} else if (i >= 14 && i <= 16) {
JP7.add(bt[i]);
} else if ((i >= 17 && i <= 17)||(i>=2&&i<=3)) {
JP8.add(bt[i]);
}
}
//添加标签和文本框到面板上
JP1.add(JL1);
JP1.add(JA1);
JP2.add(JL2);
JP2.add(JT2);
//设置文本区格式
JA1.setFont(new Font("行书", Font.PLAIN, 30));
JT2.setFont(new Font("行书", Font.PLAIN, 30));
bt[1].setEnabled(false);//刚开始设置退格键不能操作
this.add(JP1);
this.add(JP2);
this.add(JP3);
this.add(JP4);
this.add(JP5);
this.add(JP6);
this.add(JP7);
this.add(JP8);
}
/**@事件监听方法*/
@Override
public void actionPerformed(ActionEvent e) throws EmptyStackException {
if(e.getSource()==bt[0]){
//点击了清除键,将文本区清除
JA1.setText("");
JT2.setText("");
JA1.setEnabled(true);
isJA1empty();
}
else if(e.getSource()==bt[1]){
//截取需退格字符前面的字符串
int length=JA1.getText().length();
JA1.setText(JA1.getText().substring(0,length-1));
length--;
isJA1empty();
}
else if(e.getSource()==bt[2]){
JA1.append(bt[2].getText());
isJA1empty();
}
else if(e.getSource()==bt[3]){
JA1.append(bt[3].getText());
isJA1empty();
}
else if(e.getSource()==bt[4]){
JA1.append(bt[4].getText());
isJA1empty();
}
else if(e.getSource()==bt[5]){
JA1.append(bt[5].getText());
isJA1empty();
}
else if(e.getSource()==bt[6]){
JA1.append(bt[6].getText());
isJA1empty();
}
else if(e.getSource()==bt[7]){
try {
isJA1empty();
} catch (Exception ex) {
throw new EmptyStackException();
}
try {
isCharacter();
} catch (Exception ex) {
}
}
else if(e.getSource()==bt[8]){
JA1.append(bt[8].getText());
isJA1empty();
}
else if(e.getSource()==bt[9]){
JA1.append(bt[9].getText());
isJA1empty();
}
else if(e.getSource()==bt[10]){
JA1.append(bt[10].getText());
isJA1empty();
}
else if(e.getSource()==bt[11]){
JA1.append(bt[11].getText());
isJA1empty();
}
else if(e.getSource()==bt[12]){
JA1.append(bt[12].getText());
isJA1empty();
}
else if(e.getSource()==bt[13]){
JA1.append(bt[13].getText());
isJA1empty();
}
else if(e.getSource()==bt[14]){
JA1.append(bt[14].getText());
isJA1empty();
}else if(e.getSource()==bt[15]){
JA1.append(bt[15].getText());
isJA1empty();
}else if(e.getSource()==bt[16]){
JA1.append(bt[16].getText());
isJA1empty();
}
else if(e.getSource()==bt[17]){
JA1.append(bt[17].getText());
isJA1empty();
}
/*else if(e.getSource()==bt[18]){
JA1.append(bt[2].getText());
isJA1empty();
}*/
}//事件监听方法结束
public void isJA1empty() throws EmptyStackException{
//判断文本框是否为空,控制退格键是否可以操作
if("".equals(JA1.getText())){
bt[1].setEnabled(false);
}else{
bt[1].setEnabled(true);
}
}
public void isCharacter(){
if(JA1.getText().charAt(0)=='+'||JA1.getText().charAt(0)=='-'||JA1.getText().charAt(0)=='*'||JA1.getText().charAt(0)=='/'||JA1.getText().charAt(0)=='%'){
JT2.setText("无效输入");
JA1.setEnabled(false);
}else{
try {
JT2.setText(Double.toString(judgeExpression(JA1.getText())));
} catch (Exception e) {
throw new EmptyStackException();
}
}
}//判断表达式首位是否为运算符,若是,则无效输入
/**@此方法用空格分隔数值和运算符,以便后面拆分字符为字符串数组*/
public String delCharacter(String s){
String result="";
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='+'||s.charAt(i)=='-'||s.charAt(i)=='*'||s.charAt(i)=='/'||s.charAt(i)=='%'||s.charAt(i)=='('||s.charAt(i)==')'){
result+=" "+s.charAt(i)+" ";
}else{
result+=s.charAt(i);
}
}
//System.out.println(result);
return result;
}//表达式拆分完毕
/**@开始进行对数值和运算符的入栈操作判断*/
public double judgeExpression(String expression){
//创建两个栈-数据栈和运算符栈
Stack<Double> numStack=new Stack<>();
Stack<Character> charStack=new Stack<>();
//处理表达式
expression=delCharacter(expression);
//通过给定的正则表达式将字符串分割成字符串数组
String []strs=expression.split(" ");
/**开始对字符进行判断入栈操作*/
for(String str:strs){
if(str.length()==0){//如果是空格的话,什么也不操作,直接进行下一步
continue;
}else if(str.charAt(0)=='+'||str.charAt(0)=='-'){
//根据入栈规则,加减优先级最低,如果栈不为空,栈顶的运算符优先级一定大于等于目前运算符,所以直接弹出栈顶运算符进行运算
while(!charStack.isEmpty()&&(charStack.peek()=='+'||charStack.peek()=='-'||charStack.peek()=='*'||charStack.peek()=='/'||charStack.peek()=='%')){
calExpression(numStack,charStack);//开始运算
}
charStack.push(str.charAt(0));//运算完毕后,将目前运算符入栈
}//加减入栈判断操作结束
else if(str.charAt(0)=='*'||str.charAt(0)=='/'||str.charAt(0)=='%'){
//因为乘除优先级高于加减,所以要对栈顶的运算符进行判断是否为乘除,是则弹出进行运算,否则将目前运算符压入栈中
while(!charStack.isEmpty()&&(charStack.peek()=='*'||charStack.peek()=='/'||charStack.peek()=='%')){
calExpression(numStack,charStack);//开始运算
}
charStack.push(str.charAt(0));//运算完毕后,将目前运算符入栈
}else if(str.trim().charAt(0)=='('){
charStack.push('(');
}//如果是左括号则直接压入栈中
else if (str.trim().charAt(0)==')') {//如果是右括号则直到左括号的运算符全部出栈
while(charStack.peek()!='('){
calExpression(numStack,charStack);//开始运算
}
charStack.pop();//运算结束后,让左括号出栈
}else{//如果是数字的话,无需判断,直接入数据栈
numStack.push(Double.parseDouble(str));//parseInt是将数字字符直接转化为整型的方法,也可以用ascii表进行转换
}
}/**运算符判断循环结束*/
//最后将栈中剩余的运算符出栈运算,知道栈空即可
while(!charStack.isEmpty()){
calExpression(numStack,charStack);//开始运算
}
try {
return numStack.peek();//数据栈中最后的数就是运算结果
} catch (Exception e) {
throw new EmptyStackException();
}
}
/**@此方法是对符合出栈规则的运算表达式进行计算*/
public void calExpression(Stack<Double> numStack,Stack<Character> charStack){
char op=charStack.pop();//弹出一个运算符
double num1=numStack.pop();
double num2=numStack.pop();//弹出两个数值
if(op=='+'){
numStack.push(num1+num2);
}else if(op=='-') {
numStack.push(num2-num1);
}else if(op=='*'){
numStack.push(num2*num1);
}else if(op=='/'){
if(num1!=0) {
numStack.push(num2 / num1);
}else{
JA1.setText("被除数不能为0");
}
}else if(op=='%'){
numStack.push(num2%num1);
}//栈结构,后面数字在上面,先出栈,所以除法和减法num1在后
}
public static void main(String[] args){
new Main();
}
}
在写计算器之前,我还没有学过图形化和栈,借鉴了很多大佬的想法和思路,然后我对自己敲的时候遇到的问题做了一个总结。
首先,我先一步步解析代码。
首先是图形化界面基础设置部分。
我们按照一个规范的类来写。
1.首先是对成员变量的声明于实例化。
public class Main extends JFrame implements ActionListener{
/**@*************成员变量的声明与实例化**************/
//声明按钮数组
JButton bt[]=new JButton[18];
//面板
JPanel JP1=new JPanel();
JPanel JP2=new JPanel();
JPanel JP3=new JPanel();
JPanel JP4=new JPanel();
JPanel JP5=new JPanel();
JPanel JP6=new JPanel();
JPanel JP7=new JPanel();
JPanel JP8=new JPanel();
//标签和文本框
JLabel JL1=new JLabel("输入框:");
JLabel JL2=new JLabel("输出框:");
JTextArea JA1=new JTextArea(1/2,12);
JTextField JT2=new JTextField(12);
/**@**************成员变量的声明与实例化************/
这里的成员变量包括18个按钮,8个面板,两个标签(输入框和输出框)作为提示,以及文本域和文本框(这两个在实力过程中就顺便调整大小参数)。
2.接下来就是写实现类的构造方法(构造器)。
public Main(){
initialize();//初始化容器方法
//窗体设置
this.setTitle("计算器");
this.setBounds(600,200,320,500);
this.setSize(500,500);
this.setVisible(true);
this.setLayout(new GridLayout(8,1));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
这里就有可能会有同学要问了。为什么我没有创建JFrame窗体,就可以用this.直接初始化窗体参数?
这个问题其实很简单,但是很多资料都没说到过,包括我的课本,我查了好久。
JFrame窗体的创建有两种方法。
一、构造方法创建。
import javax.swing.*;
public class windowJFame_JDialog extends JFrame{
public windowJFrame_JDialog() {
}
}
在主类中继承JFrame这个类,那么他的构造方法就是一个窗体。在这个构造方法中我们无需再实例化JFrame这个类,我们可以直接调用关于JFrame类中的方法,例如设置大小和坐标。
而this在这里就是这个类的一个实例化对象。
二、实例化创建
public static void main(String[] args){
JFrame jf = new JFrame();// 实例化JFrame窗体
}
我们在main方法中实例化了JFrame对象,使用jf来接收这个对象;这样jf就是一个窗体,在运行时就会实现这个窗体(前提设置窗体可见);
在实例化方式创建完窗体后,将不再提供默认对象了,也就是不能直接调用JFrame中的方法!在需要调用方法的时候我们需要借助接收的jf这个变量对象来调用JFrame中的方法;
这里引用了文章:https://blog.youkuaiyun.com/ZunXin_2580/article/details/118274548?spm=1001.2014.3001.5506
可以作为了解。
然后作为构造方法,我们在实例化对象的时候,这个类第一时间会先运行构造方法,所以窗体的基本设置最好放在构造方法里面。
布局管理器我选择的是网格布局管理器。我试过其它的管理器,最美观的是盒子布局管理器,也挺好用的。
3.接下来我们开始成员方法的编写。
/**@初始化方法*/
public void initialize() {
//初始化与添加按钮
String buttonName[] = {"清空", "退格", "%", "/", "*","+","-", "=", "7","8","9", "4","5" ,"6", "1", "2", "3", "0"};
for (int i = 0; i < buttonName.length; i++) {
bt[i] = new JButton(buttonName[i]);
bt[i].setForeground(Color.BLUE);//设置按钮背景颜色
bt[i].setFont(new Font("行书", Font.PLAIN, 40));//设置文字样式
bt[i].addActionListener(this);//为此时的按钮添加监听器
//添加按钮到面板上
if (i >= 0 && i <= 1) {
JP3.add(bt[i]);
} else if (i >= 4 && i <= 7) {
JP4.add(bt[i]);
} else if (i >= 8 && i <= 10) {
JP5.add(bt[i]);
} else if (i >= 11 && i <= 13) {
JP6.add(bt[i]);
} else if (i >= 14 && i <= 16) {
JP7.add(bt[i]);
} else if ((i >= 17 && i <= 17)||(i>=2&&i<=3)) {
JP8.add(bt[i]);
}
}
//添加标签和文本框到面板上
JP1.add(JL1);
JP1.add(JA1);
JP2.add(JL2);
JP2.add(JT2);
//设置文本区格式
JA1.setFont(new Font("行书", Font.PLAIN, 30));
JT2.setFont(new Font("行书", Font.PLAIN, 30));
bt[1].setEnabled(false);//刚开始设置退格键不能操作
this.add(JP1);
this.add(JP2);
this.add(JP3);
this.add(JP4);
this.add(JP5);
this.add(JP6);
this.add(JP7);
this.add(JP8);
}
为了让代码看的简洁,我们把组件的初始化设置单独写成一个方法,在构造方法中进行调用。
我们首先对按钮进行一一命名,然后通过for循环进行初始化按钮参数并把按钮添加到面板上。
我们在成员变量的声明与实例化那里实例化了8个面板,对应内容面板上的八行:前两行添加标签和文本,后六行添加按钮。
同时我们可以通过setFont()方法对文本内容进行设置,关于这个Font方法,可以参考:
(8条消息) Java中----Font类简介_java font_锥子A的博客-优快云博客
感谢大佬的总结!
别忘了,我们的退格键刚开始没有输入的时候,是不允许使用的,所以我们在初始化时要先关闭它。
此时,我们已经把标签,文本框,按钮这些小组件一一安装到面板上。最后我们只需把所有的面板都添加到窗体里面去,这样,我们的图形化界面就大功告成啦!!!
然后是事件监听部分。
/**@事件监听方法*/
@Override
public void actionPerformed(ActionEvent e) throws EmptyStackException {
if(e.getSource()==bt[0]){
//点击了清除键,将文本区清除
JA1.setText("");
JT2.setText("");
JA1.setEnabled(true);
isJA1empty();
}
else if(e.getSource()==bt[1]){
//截取需退格字符前面的字符串
int length=JA1.getText().length();
JA1.setText(JA1.getText().substring(0,length-1));
length--;
isJA1empty();
}
else if(e.getSource()==bt[2]){
JA1.append(bt[2].getText());
isJA1empty();
}
else if(e.getSource()==bt[3]){
JA1.append(bt[3].getText());
isJA1empty();
}
else if(e.getSource()==bt[4]){
JA1.append(bt[4].getText());
isJA1empty();
}
else if(e.getSource()==bt[5]){
JA1.append(bt[5].getText());
isJA1empty();
}
else if(e.getSource()==bt[6]){
JA1.append(bt[6].getText());
isJA1empty();
}
else if(e.getSource()==bt[7]){
try {
isJA1empty();
} catch (Exception ex) {
throw new EmptyStackException();
}
try {
isCharacter();
} catch (Exception ex) {
}
}
else if(e.getSource()==bt[8]){
JA1.append(bt[8].getText());
isJA1empty();
}
else if(e.getSource()==bt[9]){
JA1.append(bt[9].getText());
isJA1empty();
}
else if(e.getSource()==bt[10]){
JA1.append(bt[10].getText());
isJA1empty();
}
else if(e.getSource()==bt[11]){
JA1.append(bt[11].getText());
isJA1empty();
}
else if(e.getSource()==bt[12]){
JA1.append(bt[12].getText());
isJA1empty();
}
else if(e.getSource()==bt[13]){
JA1.append(bt[13].getText());
isJA1empty();
}
else if(e.getSource()==bt[14]){
JA1.append(bt[14].getText());
isJA1empty();
}else if(e.getSource()==bt[15]){
JA1.append(bt[15].getText());
isJA1empty();
}else if(e.getSource()==bt[16]){
JA1.append(bt[16].getText());
isJA1empty();
}
else if(e.getSource()==bt[17]){
JA1.append(bt[17].getText());
isJA1empty();
}
/*else if(e.getSource()==bt[18]){
JA1.append(bt[2].getText());
isJA1empty();
}*/
}//事件监听方法结束
关于事件监听,我觉得两篇文章不错: (8条消息) java的事件监听_java监听事件和处理事件_晓之木初的博客-优快云博客
(8条消息) (JButton) e.getSource();是什么意思_爽快的方方面面的博客-优快云博客
至于e.getSource()方法,可以简单理解为“得到事件发生的信息,在这里也就是你点击了哪个按钮?,并通过if-else分支来对应判断。
判断成功之后,通过append()方法,把获取到的按钮文本追加到JA1文本框里面。这样就实现了,通过按钮对表达式的添加。
至于isJA1empty()方法,它是一个判空方法,来控制退格键是否可以操作。
public void isJA1empty() throws EmptyStackException{
//判断文本框是否为空,控制退格键是否可以操作
if("".equals(JA1.getText())){
bt[1].setEnabled(false);
}else{
bt[1].setEnabled(true);
}
}
敲重点!!!
注意bt[7],这是一个”=“按钮,当我们点击等号的时候,说明计算开始,并且等号不会追加到文本框。方便大家看,我再显示一次:
else if(e.getSource()==bt[7]){
try {
isJA1empty();
} catch (Exception ex) {
throw new EmptyStackException();
}
try {
isCharacter();
} catch (Exception ex) {
}
}
我们调用了两个成员方法:isJA1empty()和isCharacter()。
方法1就不多说了,方法二看这里:
public void isCharacter(){
if(JA1.getText().charAt(0)=='+'||JA1.getText().charAt(0)=='-'||JA1.getText().charAt(0)=='*'||JA1.getText().charAt(0)=='/'||JA1.getText().charAt(0)=='%'){
JT2.setText("无效输入");
JA1.setEnabled(false);
}else{
try {
JT2.setText(Double.toString(judgeExpression(JA1.getText())));
} catch (Exception e) {
throw new EmptyStackException();
}
}
}//判断表达式首位是否为运算符,若是,则无效输入
”=“的输入,说明要把表达式进行提交计算了。这个时候我们再去检查表达式的首位是否出现运算符的情况,如果出现,这明显是无效表达式。
如果表达式正确,我们再进行计算:
JT2.setText(Double.toString(judgeExpression(JA1.getText())));详解:
judgeExpression(String s)是计算表达式的方法,参数是一个字符串,通过文本框得到字符串,进行计算。再将计算结果Double类型转化为字符串类型,输出到输出框里面。
这里面出现了很多关于异常抛出的代码。我们最后再说。
通过栈实现混合运算
这里对初学者可能是一个难点,但更多的是找不到好的资料,这里作者我找了好久,已经备好了我认为最好理解的文章和视频:
栈实现混合运算的原理--转自bilibili:
(8条消息) 简易计算器(详解用栈实现算术表达式求值)-优快云博客
以及正则表达式:
(8条消息) 什么是正则表达式 ?_肥宅不死的博客-优快云博客
关于正则表达式可以稍作了解即可,有兴趣可以深究。
好了,把准备工作做完,就可以开始写代码啦!
/**@此方法用空格分隔数值和运算符,以便后面拆分字符为字符串数组*/
public String delCharacter(String s){
String result="";
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='+'||s.charAt(i)=='-'||s.charAt(i)=='*'||s.charAt(i)=='/'||s.charAt(i)=='%'||s.charAt(i)=='('||s.charAt(i)==')'){
result+=" "+s.charAt(i)+" ";
}else{
result+=s.charAt(i);
}
}
//System.out.println(result);
return result;
}//表达式拆分完毕
首先,我们用空格将表达式把数字和运算符进行拆分,并把最后拆分好的字符串赋值给result,并返回。结果是这样的:5+4➡5 + 4
然后我们开始通过两个栈--数据栈和运算符栈判断优先级,并得到后缀表达式。
/**@开始进行对数值和运算符的入栈操作判断*/
public double judgeExpression(String expression){
//创建两个栈-数据栈和运算符栈
Stack<Double> numStack=new Stack<>();
Stack<Character> charStack=new Stack<>();
//处理表达式
expression=delCharacter(expression);
//通过给定的正则表达式将字符串分割成字符串数组
String []strs=expression.split(" ");
/**开始对字符进行判断入栈操作*/
for(String str:strs){
if(str.length()==0){//如果是空格的话,什么也不操作,直接进行下一步
continue;
}else if(str.charAt(0)=='+'||str.charAt(0)=='-'){
//根据入栈规则,加减优先级最低,如果栈不为空,栈顶的运算符优先级一定大于等于目前运算符,所以直接弹出栈顶运算符进行运算
while(!charStack.isEmpty()&&(charStack.peek()=='+'||charStack.peek()=='-'||charStack.peek()=='*'||charStack.peek()=='/'||charStack.peek()=='%')){
calExpression(numStack,charStack);//开始运算
}
charStack.push(str.charAt(0));//运算完毕后,将目前运算符入栈
}//加减入栈判断操作结束
else if(str.charAt(0)=='*'||str.charAt(0)=='/'||str.charAt(0)=='%'){
//因为乘除优先级高于加减,所以要对栈顶的运算符进行判断是否为乘除,是则弹出进行运算,否则将目前运算符压入栈中
while(!charStack.isEmpty()&&(charStack.peek()=='*'||charStack.peek()=='/'||charStack.peek()=='%')){
calExpression(numStack,charStack);//开始运算
}
charStack.push(str.charAt(0));//运算完毕后,将目前运算符入栈
}else if(str.trim().charAt(0)=='('){
charStack.push('(');
}//如果是左括号则直接压入栈中
else if (str.trim().charAt(0)==')') {//如果是右括号则直到左括号的运算符全部出栈
while(charStack.peek()!='('){
calExpression(numStack,charStack);//开始运算
}
charStack.pop();//运算结束后,让左括号出栈
}else{//如果是数字的话,无需判断,直接入数据栈
numStack.push(Double.parseDouble(str));//parseInt是将数字字符直接转化为整型的方法,也可以用ascii表进行转换
}
}/**运算符判断循环结束*/
//最后将栈中剩余的运算符出栈运算,知道栈空即可
while(!charStack.isEmpty()){
calExpression(numStack,charStack);//开始运算
}
try {
return numStack.peek();//数据栈中最后的数就是运算结果
} catch (Exception e) {
throw new EmptyStackException();
}
}
我们先创建两个栈,分别储存数字和运算符。(要注意栈的泛型Double类型和char类型)
然后将表达式分割。
然后通过spilt()方法通过给定的正则表达式把字符串拆分为字符串数组。
再对字符串数组进行遍历,对字符进行判断入栈操作:
入栈的规则,在上面的视频里讲的很清楚,只有当栈顶的运算符优先级小于当前运算符,当前运算符可以直接入栈,否则要弹出栈顶的运算符,再弹出数据栈中的两个数字,组成后缀表达式,进行运算,并将计算结果压入数据栈中,最后再把当前运算符入栈。如果遇到左括号,就直接压入栈中,遇到右括号,就将右括号之前的运算符出栈并进行运算,直到遇到左括号,计算结果压入数据栈中,并让左括号出栈。当表达式遍历完后,让栈中剩余的运算符一一出栈进行运算,直到站空为止。数据栈中最后的数字,就是最终计算结果。
这里是运算方法:
public void calExpression(Stack<Double> numStack,Stack<Character> charStack){
char op=charStack.pop();//弹出一个运算符
double num1=numStack.pop();
double num2=numStack.pop();//弹出两个数值
if(op=='+'){
numStack.push(num1+num2);
}else if(op=='-') {
numStack.push(num2-num1);
}else if(op=='*'){
numStack.push(num2*num1);
}else if(op=='/'){
if(num1!=0) {
numStack.push(num2 / num1);
}else{
JA1.setText("被除数不能为0");
}
}else if(op=='%'){
numStack.push(num2%num1);
}//栈结构,后面数字在上面,先出栈,所以除法和减法num1在后
}
除法的话要考虑被除数不能为0。
最后解释一下异常处理。
还记得我们算出最终结果的原理吗?
对,数据栈中最后的一个数字就是最终计算结果。但如果被除数为0,按照我们上面的if判断,我们的文本框会打印”被除数不能为0“。但是,没有计算结果,这个时候数据栈是空的,那我们在想从栈中得到计算结果并打印在文本框时,会出现空栈异常。
有两种解决方案:
一、通过抛出异常,解决在运行中出现Expection异常。
二、当出现被除数为0时,我们手动想栈中压入一个数字,来代表计算出错,但是会覆盖提示”被除数不能为0“。