目录
正文
项目前提
项目思路
项目实现
一:实现图形化界面的设计
二:关于按钮监听及文本框展示的实现
三:表达式运算的实现
四:完整代码
作者的话
正文
项目前提
~掌握java基本语法
~熟悉基础数据结构的运用
~了解Java Swing 和Java awt包下的一些类的作用
项目思路
~利用java内置的GUI实现计算器界面的设置
~利用栈实现表达式中缀转后缀实现对于表达式的运算
项目实现
一:实现图形化界面的设计
1,首先创建一个类,让其继承于javax下的JFame类,这样方便直接使用JFame中的方法
2,先创建一个窗口对象jfm(注意:因为这个项目只需要单窗口所以也可以使用this关键字作为窗口对象,但其他多窗口项目的话建议创建JFame对象,这样可以方便实现窗口之间的切换),然后创建一个方法设置窗口的长宽,默认位置,关闭窗口等各种属性
3,设置窗口的各种按钮(需添加数字1~9,小数点,运算符号,括号,等号,清除删除键等等),并往相应按钮中添加相应字符,便于向用户展示相字符的作用(此处不含按钮监听事件),同时也可以优化按钮设置如数字0~9用同一个颜色,运算符用一个颜色,等号用为一颜色用于区分,关于按钮布局可以模仿微软计算器中的布局。
4,设置两个文本框用于展示输入运算表达式,和输出结果,需设值文本框为不可输入,呈现表达式只需向文本框中添加相应的字符串即可。
以下为图像化界面实现的相应代码(这段是从完整代码中截取下来的,如单独使用可能会产生一些BUG,要使用代码可以直接复制下方完整代码,下同):
public class MYButton extends JFrame {//创建stack栈用于输入内容public static Stack stack = new Stack();//创建窗口对象public static JFrame jfm = new JFrame();//设置窗口的高度和宽度,用final关键字防止数据发生改变private static final int FRAME_HIGH = 560;private static final int FRAME_LENtH = 400;//创建字符串数组,用于添加到按钮中方便用户辨别按钮作用private static final String[] s = {"(", ")", "CE", "Del", "7", "8", "9", "/", "4", "5", "6", "x", "1", "2", "3", "-", ".", "0", "=", "+"};//声名按钮数组public static JButton Jbt[] = new JButton[20];//创建文本输入框用于输入输出public static JTextField Input = new JTextField();public static JTextField Output = new JTextField();public static boolean Point=true;//构造空参方法public MYButton() {InitButton();InitFrame();InitField();Input();print();jfm.setVisible(true);}//方法:初始化按钮private static void InitButton() {//设置字体Font f = new Font("宋体", Font.BOLD, 20);//将相应字符串加载到相应按钮中for (int i = 0; i < s.length; i++) {Jbt[i] = new JButton(s[i]);Jbt[i].setSize(90, 55);Jbt[i].setFont(f);}//改变按钮背景颜色,美化按钮for (int i = 0; i < 20; i++) {int x = 5 + (i % 4) * 95;int y = 225 + (i / 4) * 60;Jbt[i].setLocation(x, y);if (i / 4 < 1 || i % 4 == 3) {Jbt[i].setBackground(new Color(230, 246, 255));} else if (i == 18) {Jbt[i].setBackground(new Color(202, 139, 72));} else Jbt[i].setBackground(Color.WHITE);Jbt[i].setFocusPainted(false);//添加按钮到窗口中MYButton.jfm.getContentPane().add(Jbt[i]);}}//方法:初始化文本输入框private void InitField() {//设置字体Font f = new Font("微软雅黑", Font.CENTER_BASELINE, 30);//设置文本框的相应属性Input.setSize(375, 100);Input.setLocation(5, 5);Input.setFont(f);Input.setBackground(new Color(255, 239, 141));Input.setEnabled(true);Input.setHorizontalAlignment(4);//禁止文本框自行输入Input.setEnabled(false);Output.setFont(f);Output.setSize(375, 100);Output.setLocation(5, 110);Output.setBackground(new Color(202, 255, 215));Output.setHorizontalAlignment(4);Output.setEnabled(false);//将文本框添加到窗口中jfm.getContentPane().add(Input);jfm.getContentPane().add(Output);}//初始化窗口private void InitFrame() {//设置窗口的相应属性jfm.setSize(FRAME_LENtH, FRAME_HIGH);jfm.setDefaultCloseOperation(EXIT_ON_CLOSE);jfm.setLayout(null);jfm.setLocationRelativeTo(null);jfm.getContentPane().setBackground(new Color(246, 240, 249));jfm.setTitle("MyCalculator");jfm.setResizable(false);}}
二:关于按钮监听及文本框展示的实现
1:该项目的按钮实现不用过于复杂只需要监听鼠标点击即可(当然也可以采用键盘监听,本文以鼠标点击监听为主),按钮采用ActionListener 事件监听,由于在实现监听是需要在按钮中添加ActionListener接口的实现类对象,为了避免代码地冗余,所以此处应创建一个匿名内部类用于实现按钮监听。
2:由于已经限制了文本框的输入,所以若想实现文本框展示表达式只需要向文本框中添加相应的字符串。所以首先思路是当点击按钮后将按钮中的文本信息储存并添加到文本框中,由于该项目用到了删除清空功能,所以可以采用栈进行存储,当点击到不是删除清空时就直接将其存储到栈中,如点击删除则出栈,点击清空时就将栈置空,该功能可以很好的保证表达式在文本框中的动态变化。
3:由于存储字符串时使用的是栈,所以可以同时实现括号匹配问题的判断,某些错误表达式输入的限制如两个运算符不能相邻输入 :(1+/2)该表达式就是错误的,此时只需要在输入前查看栈顶元素并与输入的元素进行判断即可。
代码展示:
//方法,监听按钮并将表达式呈现在文本框中private void Input() {for (int i = 0; i < MYButton.Jbt.length; i++) {int finalI = i;//使用匿名内部类Jbt[i].addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("djl");//当点击的按钮为“CE”时,清空栈if (Jbt[finalI].getText().equals("CE")) {MYButton.Point=true;while (!MYButton.stack.empty()) {MYButton.stack.pop();}MYButton.Output.setText("");}//为数字或运算符号时入栈else if (!Jbt[finalI].getText().equals("Del")&!Jbt[finalI].getText().equals("=")&&!Output.getText().equals("Error")) {String s1 = Jbt[finalI].getText();if(!stack.empty()) {String sss = MYButton.stack.peek();if (!((sss.equals("+") || sss.equals("-") || sss.equals("/") || sss.equals("x")||sss.equals("(")) &&(s1.equals("+") || s1.equals("/") || s1.equals("x") || s1.equals("-")||s1.equals(")")))) {if((!((sss.equals(")"))&& !(s1.equals("+") || s1.equals("/") || s1.equals("x") || s1.equals("-")||s1.equals(")"))))&&(!((!(sss.equals("+") || sss.equals("/") || sss.equals("x") || sss.equals("-")||sss.equals(")")||sss.equals("(")))&&(s1.equals("("))))) {MYButton.stack.push(Jbt[finalI].getText());System.out.println(Jbt[finalI].getText());}} else {System.out.println("你干嘛");}}else{if(!(s1.equals("+") || s1.equals("/") || s1.equals("x") || s1.equals("-")||s1.equals(")"))) {MYButton.stack.push(Jbt[finalI].getText());System.out.println(Jbt[finalI].getText());}else {System.out.println("你干嘛");}}}//为等号时,运算表达式并将答案呈现在输出框中if(Jbt[finalI].getText().equals("=")&&!Output.getText().equals("Error")) {if(!stack.empty()) {ArrayList List = new ArrayList();AddList(List);System.out.println(MYButton.Point);if (if_$(List)) {calculator.TurnTo(stack);if(MYButton.Point){System.out.println("111"+MYButton.Point);float f = calculator.calculate(calculator.TurnTo(MYButton.stack));Output.setText("=" + String.valueOf(f));}else MYButton.Output.setText("Error");} else MYButton.Output.setText("Error");}}//为删除符号时移除栈顶元素else if (!MYButton.stack.empty()&Jbt[finalI].getText().equals("Del")&&!Output.getText().equals("Error"))System.out.println(MYButton.stack.pop());//展示表的时的方法要在监听事件的匿名内部类中调用,否则输入框中将无展示的信息print();}});}}//将栈中的字符串拼接并呈现在输入框中private void print(){String str = "";if (!MYButton.stack.empty()) {//用foreach进行遍历栈并不会销毁栈,若用pop方法则会将栈销毁for (String c : MYButton.stack) {str = str + c;}}//设置文本框的Text为拼接后的字符串MYButton.Input.setText(str);System.out.println(MYButton.Input.getText());}private void AddList(ArrayList list){for (String str:MYButton.stack ) {if(str.equals("(")||str.equals(")"))list.add(str);}}//判断表达式的括号匹配问题privateboolean if_$(ArrayList list) {Stack s=new Stack();for (int i = 0; i < list.size(); i++) {System.out.println("qqq"+list.get(0));if(list.get(i).equals("(")){s.push(list.get(i));}else if(list.get(i).equals(")")){if(!s.empty()) {if (!$_if(s.pop(), list.get(i))) return false;}else return false;}}if(s.empty())return true;else return false;}private boolean $_if(String str1,String str2) {if(str1.equals("(")&&str2.equals(")"))return true;else return false;}
三:表达式运算的实现
1:此处将表达式中缀转化为后缀进行计算(详情可看数据结构相关书籍)
2:关于小数点的实现,当读取输入字符串时(此处为文本框中的表达式)如123.4+23
当读取时含有 “1” ,”2″ ,”3″ ,”.” ,”4″ ,”+” ,”2″ ,”3″这几个字符,此时只需要将是数字及小数点的字符串进行拼接即可,就像该表达式当读取到 1 2 3 . 4时直接拼接字符串,当读取到 + 时停止拼接,并且该方法也能在拼接过程中判断小数点的个数,如果小数点的个数大于一个时可以限制小数点的输入。
3:在进行后缀表达式的计算式,只需要将数字字符串先转化为float类型的数,然后运用一个switch-case方法判断运算符类型进行计算即可,注意:由于计算机无法实现0.3,0.03,0.0…..03的判断,此处需要先将表达式的数字扩大一定的倍数运算完后缩小一定的倍数即可
如 在没有扩大倍数时
在扩大了1000倍后
四:完整代码
calculator类
package com.mycalulator;import java.util.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import static java.lang.Math.*;public class calculator extends MYButton {//表达式的运算public calculator(){}//方法中缀转后缀public static ArrayList TurnTo(Stack stack) { //声名Arraylist的字符串集合用来存储后缀表达式ArrayList list=new ArrayList();//存储这正常表达式的栈Stack TempStack = new Stack();//工具栈用于改变操作符的位置Stack StackTemp = new Stack();//中转栈,用于foreach方法是从栈顶开始遍历的,如果不用中转栈存储的表达式的顺序会发生颠倒Stack stringStack=new Stack();//将表达式存入到栈中String str = "";for (String s:stack ) {stringStack.push(s);} while(!stringStack.empty()) { TempStack.push(stringStack.pop()); } //中缀转后缀的代码块while (!TempStack.empty()) {int PointCount=0;//去出存储栈的栈顶元素String temp = TempStack.pop();//存储数字while ((!temp.equals("x")) &(!temp.equals("+"))& (!temp.equals("-")) &(!temp.equals("/")) &(!temp.equals("("))& (!temp.equals(")"))) {//循环判断是否为操作符,能够实现数字的拼接,例如出栈元素若为 1,2,3,.,4,则可将其拼接为123.4,改步骤能实现小数点的加入以及非个位数的输入str = str + temp;if(!TempStack.empty()) {temp = TempStack.pop();}//如果最后一个元素为数字的话,会出现死循环,所以加入改代码可以及时跳出循环else {temp="exit";break;}}//判断是否有数字输入,如有则添加进集合中if (!str.equals("")) {list.add(str);System.out.println(Change(str));if(!Change(str))MYButton.Point=false;str="";if(temp.equals("exit"))break;}//判断是否为操作符if(!temp.equals("(")&&!temp.equals(")")) {//如果工具栈为空,则直接存入数字if (StackTemp.empty()) {StackTemp.push(temp);}else {if(!StackTemp.empty()) {//比较工具栈栈顶操作符和即将输入操作符的优先级String str2 = StackTemp.pop();//判断工具栈顶是否为(if (!str2.equals("(")) {if (bos(str2, temp)) {//如果工具栈顶元素优先级大于输入操作符,则将改元素添加到集合中list.add(str2);//将输入的操作符从新添加到存储栈中用于下一次比较TempStack.push(temp);}else {StackTemp.push(str2);StackTemp.push(temp);}}//如果没有达到添加条件,则将输入元素压入工具栈中else {StackTemp.push(str2);StackTemp.push(temp);}}}}//如果输入元素为”(“则直接压入工具栈中if(temp.equals("(")) StackTemp.push(temp);//如果输入元素为大括号则将工具栈中栈顶的操作符持续添加到集合中,知道读取元素为"("时停止else if(temp.equals(")")){String str3=StackTemp.pop();while(!str3.equals("(")){list.add(str3);str3=StackTemp.pop();}}}//当存储栈为空时,则将工具栈中的元素全部添加到集合中while(!StackTemp.empty()) {list.add(StackTemp.pop());}for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i));}//返回改变后地集合return list;}//判断两个操作符地优先级private static boolean bos(String str1,String str2){ if((str2.equals("/")||str2.equals("x"))&&(str1.equals("+")||str1.equals("-"))){return false;}else return true;}//实现后缀表达式的运算public static floatcalculate(ArrayList list) { //表达式地运算栈Stack FloatStack=new Stack();for (int i = 0; i < list.size(); i++) {//入果此时集合中输出的元素为数字时则压入运算栈中if(!list.get(i).equals("+")&&!list.get(i).equals("-")&&!list.get(i).equals("x")&&!list.get(i).equals("/")) {//利用方法Float.parseFloat()可将字符串转化会Float类型的数字FloatStack.push(Float.parseFloat(list.get(i)));}//如果为运算符时,则出栈栈顶的两个元素并运算else{switch (list.get(i)){//由于计算机无法计算如0.3,0.03等数字所以需要先将数字先翻倍再运算最后再将所得数字缩小相应倍数case "+":{float num1=FloatStack.pop();float num2 = (FloatStack.pop()*10000)+(num1*10000);FloatStack.push(num2/10000);break;}case "-":{float num1=FloatStack.pop();float num2=(FloatStack.pop()*10000)-(num1*10000);FloatStack.push(num2/10000);break;}case "x":{float num1=FloatStack.pop();float num2=(num1*1000)*(FloatStack.pop()*1000);FloatStack.push(num2/1000000);break;}case "/":{//由于java自身会将除数为零的表达实运算的结果转化为Infinity所以不用进行除数为零的判断float num1=FloatStack.pop();float num2=(FloatStack.pop()*10000)/(num1*10000);FloatStack.push(num2);break;}}}}//返回最终的结果return FloatStack.pop();} private static boolean Change(String str){int count=0; for (int i = 0; i < str.length(); i++) {if(str.charAt(i)=='.')count++; } if(count<=1)return true; else return false; }}
MYButton类
package com.mycalulator;import java.awt.*;import javax.swing.*;import javax.swing.border.Border;import java.awt.event.*;import java.awt.image.ImageObserver;import java.awt.image.ImageProducer;import java.security.Key;import java.util.*;public class MYButton extends JFrame {//创建stack栈用于输入内容public static Stack stack = new Stack();//创建窗口对象public static JFrame jfm = new JFrame();//设置窗口的高度和宽度,用final关键字防止数据发生改变private static final int FRAME_HIGH = 560;private static final int FRAME_LENtH = 400;//创建字符串数组,用于添加到按钮中方便用户辨别按钮作用private static final String[] s = {"(", ")", "CE", "Del", "7", "8", "9", "/", "4", "5", "6", "x", "1", "2", "3", "-", ".", "0", "=", "+"};//声名按钮数组public static JButton Jbt[] = new JButton[20];//创建文本输入框用于输入输出public static JTextField Input = new JTextField();public static JTextField Output = new JTextField();public static boolean Point=true;//构造空参方法public MYButton() {InitButton();InitFrame();InitField();Input();print();jfm.setVisible(true);}//方法:初始化按钮private static void InitButton() {//设置字体Font f = new Font("宋体", Font.BOLD, 20);//将相应字符串加载到相应按钮中for (int i = 0; i < s.length; i++) {Jbt[i] = new JButton(s[i]);Jbt[i].setSize(90, 55);Jbt[i].setFont(f);}//改变按钮背景颜色,美化按钮for (int i = 0; i < 20; i++) {int x = 5 + (i % 4) * 95;int y = 225 + (i / 4) * 60;Jbt[i].setLocation(x, y);if (i / 4 < 1 || i % 4 == 3) {Jbt[i].setBackground(new Color(230, 246, 255));} else if (i == 18) {Jbt[i].setBackground(new Color(202, 139, 72));} else Jbt[i].setBackground(Color.WHITE);Jbt[i].setFocusPainted(false);//添加按钮到窗口中MYButton.jfm.getContentPane().add(Jbt[i]);}}//方法:初始化文本输入框private void InitField() {//设置字体Font f = new Font("微软雅黑", Font.CENTER_BASELINE, 30);//设置文本框的相应属性Input.setSize(375, 100);Input.setLocation(5, 5);Input.setFont(f);Input.setBackground(new Color(255, 239, 141));Input.setEnabled(true);Input.setHorizontalAlignment(4);//禁止文本框自行输入Input.setEnabled(false);Output.setFont(f);Output.setSize(375, 100);Output.setLocation(5, 110);Output.setBackground(new Color(202, 255, 215));Output.setHorizontalAlignment(4);Output.setEnabled(false);//将文本框添加到窗口中jfm.getContentPane().add(Input);jfm.getContentPane().add(Output);}//初始化窗口private void InitFrame() {//设置窗口的相应属性jfm.setSize(FRAME_LENtH, FRAME_HIGH);jfm.setDefaultCloseOperation(EXIT_ON_CLOSE);jfm.setLayout(null);jfm.setLocationRelativeTo(null);jfm.getContentPane().setBackground(new Color(246, 240, 249));jfm.setTitle("MyCalculator");jfm.setResizable(false);}//方法,监听按钮并将表达式呈现在文本框中private void Input() {for (int i = 0; i < MYButton.Jbt.length; i++) {int finalI = i;//使用匿名内部类Jbt[i].addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("djl");//当点击的按钮为“CE”时,清空栈if (Jbt[finalI].getText().equals("CE")) {MYButton.Point=true;while (!MYButton.stack.empty()) {MYButton.stack.pop();}MYButton.Output.setText("");}//为数字或运算符号时入栈else if (!Jbt[finalI].getText().equals("Del")&!Jbt[finalI].getText().equals("=")&&!Output.getText().equals("Error")) {String s1 = Jbt[finalI].getText();if(!stack.empty()) {String sss = MYButton.stack.peek();if (!((sss.equals("+") || sss.equals("-") || sss.equals("/") || sss.equals("x")||sss.equals("(")) &&(s1.equals("+") || s1.equals("/") || s1.equals("x") || s1.equals("-")||s1.equals(")")))) {if((!((sss.equals(")"))&& !(s1.equals("+") || s1.equals("/") || s1.equals("x") || s1.equals("-")||s1.equals(")"))))&&(!((!(sss.equals("+") || sss.equals("/") || sss.equals("x") || sss.equals("-")||sss.equals(")")||sss.equals("(")))&&(s1.equals("("))))) {MYButton.stack.push(Jbt[finalI].getText());System.out.println(Jbt[finalI].getText());}} else {System.out.println("你干嘛");}}else{if(!(s1.equals("+") || s1.equals("/") || s1.equals("x") || s1.equals("-")||s1.equals(")"))) {MYButton.stack.push(Jbt[finalI].getText());System.out.println(Jbt[finalI].getText());}else {System.out.println("你干嘛");}}}//为等号时,运算表达式并将答案呈现在输出框中if(Jbt[finalI].getText().equals("=")&&!Output.getText().equals("Error")) {if(!stack.empty()) {ArrayList List = new ArrayList();AddList(List);System.out.println(MYButton.Point);if (if_$(List)) {calculator.TurnTo(stack);if(MYButton.Point){System.out.println("111"+MYButton.Point);float f = calculator.calculate(calculator.TurnTo(MYButton.stack));Output.setText("=" + String.valueOf(f));}else MYButton.Output.setText("Error");} else MYButton.Output.setText("Error");}}//为删除符号时移除栈顶元素else if (!MYButton.stack.empty()&Jbt[finalI].getText().equals("Del")&&!Output.getText().equals("Error"))System.out.println(MYButton.stack.pop());//展示表的时的方法要在监听事件的匿名内部类中调用,否则输入框中将无展示的信息print();}});}}//将栈中的字符串拼接并呈现在输入框中private void print(){String str = "";if (!MYButton.stack.empty()) {//用foreach进行遍历栈并不会销毁栈,若用pop方法则会将栈销毁for (String c : MYButton.stack) {str = str + c;}}//设置文本框的Text为拼接后的字符串MYButton.Input.setText(str);System.out.println(MYButton.Input.getText());}private void AddList(ArrayList list){for (String str:MYButton.stack ) {if(str.equals("(")||str.equals(")"))list.add(str);}}//判断表达式的括号匹配问题privateboolean if_$(ArrayList list) {Stack s=new Stack();for (int i = 0; i < list.size(); i++) {System.out.println("qqq"+list.get(0));if(list.get(i).equals("(")){s.push(list.get(i));}else if(list.get(i).equals(")")){if(!s.empty()) {if (!$_if(s.pop(), list.get(i))) return false;}else return false;}}if(s.empty())return true;else return false;}private boolean $_if(String str1,String str2) {if(str1.equals("(")&&str2.equals(")"))return true;else return false;}}
项目入口类
package com.mycalulator;public class MyCalculator {public static void main(String[] args) {//创建MYButton类的对象,实现项目的开始new MYButton();}}
作者的话
由于是第一次发博客,有很多地方可能没有讲的很好请多包涵,另外如果由错误的地方可以在评论区指正,感谢大家对我的支持!!!