基於逆波蘭表達式實現圖形化混合計算器


前言

計算器在現實生活中是很普遍的一種工具,所以很多初學者基本上就會用計算器來作爲自己的第一個Java項目,當然對於我來說也並不意外。本文采用逆波蘭算法來實現計算器的功能,爲有需要的小夥伴詳細講解原理和具體實現。完整代碼可以查看GitHub倉庫

表達式求值

一、問題分解

我們把算術表達式輸入給計算器程序並得到最終的計算結果,背後應存在三個過程:

  1. 表達式合法性檢查
  2. 表達式的轉換
  3. 計算結果

因此我們可以先建立Calculator類,並加入合法性檢驗Check,表達式解析(原表達式轉換成逆波蘭表達式)Conversion和計算結果的方法Calculated,添加成員變量expression以及構造方法Calculator,用來注入和存放代表算術表達式的字符串。

主要代碼框架如下:

public class Calculator {
    //存放算術表達式
    private String expression;
    /**
     * 構造函數
     * @param expression 算術表達式
     */
    public Calculator(String expression) {
        this.expression = expression;
    }
    /**
     * @初始化
     */
    public void init() {
       
    }
    /**
     * @表達式合法性檢驗
     * @return 是否合法
     */
    public boolean Check() {
        return false or true;
    }
    /**
     * @轉化爲逆波蘭式
     * @return 存儲在Stack中的逆波蘭表達式
     */
    public Stack<String> Conversion() {
        return stack;
    }
    /**
     * 計算結果
     * @return 表達式計算結果
     */
    public String Calculated() {
        return "0";
    }
}

下面我們逐步分析並實現這些過程。

二、表達式合法性檢查

(1).合法性檢查即對用戶輸入的表達式進行檢驗,判斷其結構和語義是否存在不合法。例如我們期望的用戶輸入的合法表達式如下:

1+2
10*20-1 
-2*20+10/0.5 
(10+3)/(20-5)

(2).而不合法的表達式輸入會是這樣:

10*2/0
(10+30)/(20-5
2+3*
+6*3+5
2*-10-20

(3).諸如此類,我們可以從中總結一些有關表達式合法與否的規則:

  1. 括號必定成對出現.
  2. 除數不能爲0(由於這個不好判斷,故放在了計算的時候判斷).
  3. 運算符不能出現在表達式開始或末尾,也不能出現在左括號的右側或右括號的左側.
  4. 運算符不能連續出現.

爲了方便運算,我們將在負號前面加上0.

1.括號必須成對出現

我們先考慮括號必須成對出現的規則。這裏我們可以使用多種方法來檢驗。

  1. 我們可以設置一個計數器且初值爲0,在遍歷數組時,每當讀取到(,計數器加1;讀取到),計數器減1。任何時候計數器的值都不能小於0,且遍歷結束後計數器的值必須也隨即歸0,否則代表括號未成對出現,即表達式不合法。
  2. 我們可以使用棧(Stack)來解決該問題。在遍歷數組時,每當讀取到(,將其壓入棧;每當讀到),將棧中的(彈出棧。如果當讀取到)時棧爲空,或遍歷結束後棧未空,代表括號未成對出現,即表達式不合法。

由於方法1比較簡便,故下面採取方法1的實現:

/**
 * @表達式的合法性檢測
 * @return boolean
 */
public boolean Check() {
    //首先在負號前面補0
    for (int i = 0; i < expression.length(); i++) {
        if (expression.charAt(i) == '-' && ((i > 0 && !Character.isDigit(expression.charAt(i - 1))) || i == 0)) {
            StringBuilder exp = new StringBuilder(expression);
            exp.insert(i, "0");
            expression = exp.toString();System.out.println(expression);
        }
    }
    //判斷括號是否匹配,'('加1,')'減1
    int backets = 0;
    //從左向右掃描
    for (int i = 0; i < expression.length(); i++) {
        switch (expression.charAt(i)) {
            case '(': ++backets; break; //'('加1
            case ')': --backets; //')'減1
                if (backets < 0) //中間出現'('少於')'的情況
                    return false;
                break;
        }
    }
    if (backets != 0) //如果括號不匹配
        return false;
    return true;
}

代碼比較清晰,完全按照方法1的思路實現,並且添加了詳細註釋,不繼續贅述。

2.運算符出現位置

根據運算符不能出現在表達式開始或末尾,也不能出現在左括號的右側或右括號的左側,運算符不能連續出現,其中-是可以出現在首位的。這個我們可以利用下標,在循環遍歷的switch分支中便捷地檢驗上述情況。

/**
 * @表達式的合法性檢測
 * @return boolean
 */
public boolean Check() {
    //首先在負號前面補0
    for (int i = 0; i < expression.length(); i++) {
        if (expression.charAt(i) == '-' && ((i > 0 && !Character.isDigit(expression.charAt(i - 1))) || i == 0)) {
            StringBuilder exp = new StringBuilder(expression);
            exp.insert(i, "0");
            expression = exp.toString();System.out.println(expression);
        }
    }
    //判斷括號是否匹配,'('加1,')'減1
    int backets = 0;
    //從左向右掃描
    for (int i = 0; i < expression.length(); i++) {
        switch (expression.charAt(i)) {
            case '(': ++backets; break; //'('加1
            case ')': --backets; //')'減1
                if (backets < 0) //中間出現'('少於')'的情況
                    return false;
                break;
            case '/':
            case '*':
            case '+':
                if (i == 0) return false; // '/'、'*'、'+'不能出現首位
                if (i > 0 && (expression.charAt(i - 1) == '/' || expression.charAt(i - 1) == '*' || expression.charAt(i - 1) == '+' || expression.charAt(i - 1) == '-' || expression.charAt(i - 1) == '('))
                    return false; //運算符不能連續出現且前面不能爲'('
            case '-':
                if (i == expression.length() - 1) return false; //運算符不能出現在尾部
                if (expression.charAt(i + 1) == '/' || expression.charAt(i + 1) == '*' || expression.charAt(i + 1) == '+' || expression.charAt(i + 1) == '-' || expression.charAt(i + 1) == ')')
                    return false; //運算符不能連續出現且後面不能爲')'
        }
    }
    if (backets != 0) //如果括號不匹配
        return false;
    return true;
}

至此,算術表達式合法性檢驗方法實現完畢。

三、表達式的轉換

接下來我們來分析如何進行表達式的轉換。我們都知道四則運算的法則,先乘除後加減,有括號先算括號裏的,有多層括號先算內層括號裏的。

但是,我們並不能輕而易舉地處理一個字符串類型的算術表達式的運算優先級問題,即便是語言工具已經爲我們提供了強大的計算能力。因爲計算機並不能理解所謂四則運算優先級諸如此類的概念。此時,我們將引出一把寶刀——逆波蘭表達式。
具體的內容可詳看:波蘭式與逆波蘭式的轉換和表達式求值.

那麼知道了什麼是逆波蘭表達式,下面就進入正題。

1.先引入存放運算符優先級的Map類型成員變量,並完善構造方法:

private Map<String, Integer> priority = new HashMap<>();
/**
 * @構造方法
 * @param expression
 */
public Calculator(String expression) {
    init();
    this.expression = expression;
}
/**
 * @初始化
 */
public void init() {
    priority.put("/", 1);
    priority.put("*", 1);
    priority.put("-", 0);
    priority.put("+", 0);
    priority.put("(", -1);
    priority.put(")", -1);
    priority.put("#", -2);
}

由以上算法思想我們可以逐步實現Conversion方法來實現普通表達式到逆波蘭表達式的轉化:

/**
 * @中綴表達式轉換爲後綴表達式
 * @return Stack
 */
public Stack<String> Conversion() {
    Stack<String> s1 = new Stack<>();
    Stack<String> s2 = new Stack<>();
    s1.push("#"); //將最低優先級的#符號放入S1棧,爲了方便統一後續操作
    for (int i = 0; i < expression.length(); i++) { //循環遍歷表達式
        switch (expression.charAt(i)) {
            case '(': s1.push("("); break; //讀取到左括號,直接壓入S1棧
            case ')': //若取出的字符是")",則將距離S1棧棧頂最近的"("之間的運算符,逐個出棧,依次送入S2棧,此時拋棄"("。
                while (!s1.peek().equals("(")) {
                    s2.push(s1.pop());
                }
                s1.pop(); break;
            case '/':
            case '*':
            case '-':
            case '+':
                /*
                * 若取出的字符是運算符,則將該運算符與S1棧棧頂元素比較,
                * 如果該運算符優先級(不包括括號運算符)大於S1棧棧頂運算符優先級,則將該運算符進S1棧,
                * 否則,將S1棧的棧頂運算符彈出,送入S2棧中,直至S1棧棧頂運算符低於(不包括等於)該運算符優先級,
                * 最後將該運算符送入S1棧。
                * */
                if (priority.get(expression.charAt(i) + "") > priority.get(s1.peek())) {
                    s1.push(expression.charAt(i) + "");
                }
                else {
                    while (!(priority.get(expression.charAt(i) + "") > priority.get(s1.peek()))) {
                        s2.push(s1.pop());
                    }
                    s1.push(expression.charAt(i) + "");
                }
                break;
            default:
                //若取出的字符是操作數,則分析出完整的運算數
                StringBuilder num = new StringBuilder();
                while (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.') {
                    num.append(expression.charAt(i));
                    if (i + 1 < expression.length() && (Character.isDigit(expression.charAt(i + 1)) || expression.charAt(i + 1) == '.'))
                        ++i;
                    else break;
                }
                //該操作數直接送入S2棧
                s2.push(num.toString()); break;
        }
    }
    //將S1棧內所有運算符(不包括"#"),逐個出棧,依次送入S2棧。
    while (!s1.peek().equals("#"))
        s2.push(s1.pop());
    //由於棧的特性,S2應做一下逆序處理
    Stack<String> stack = new Stack<>();
    while (!s2.empty()) {
        stack.push(s2.pop());
    }
    return stack; //返回S2的逆序棧
}

四、計算結果

1.得到逆波蘭表達式後,我們就要計算結果了。計算方法上文已經提到,這裏方便大家再寫一遍:

  • 掃描從左往右進行,如果掃描到操作數,則壓進S,如果掃描到操作符,則從S彈出兩個操作數進行相應的操作,並將結果壓進S(S出2進1),當掃描結束後,S的棧頂就是表達式結果。

2.按照上述算法實現Calculated比較簡單,具體代碼如下:

/**
 * @逆波蘭式的求值
 * @return String
 */
public String Calculated() {
    if (!this.Check()) //合法性檢驗
        return "Error!"; //不合法返回"Error!"
    Stack<String> stack = Conversion(); //得到逆波蘭表達式
    Stack<Double> tmp = new Stack<>(); //聲明一個棧
    while (!stack.empty()) {
        String s = stack.pop(); //取出逆波蘭中的值
        if (Character.isDigit(s.charAt(0))) //如果是操作數
            tmp.push(Double.parseDouble(s)); //直接進入tmp棧
        else {//如果是運算符,取出兩個數進行計算
            double b = tmp.pop();
            double a = tmp.pop();
            switch (s) {
                case "/":
                    if (b == 0.0) //如果除數爲0,報錯
                        return "Error!";
                    tmp.push(Div(a, b)); break;
                case "*": tmp.push(Mul(a, b)); break;
                case "-": tmp.push(Sub(a, b)); break;
                case "+": tmp.push(Add(a, b)); break;
            }
        }
    }
    return tmp.pop().toString();
}
public double Div(double a, double b) {
    BigDecimal a1 = new BigDecimal(a);
    BigDecimal b1 = new BigDecimal(b);
    return a1.divide(b1, 5, BigDecimal.ROUND_HALF_UP).doubleValue();
}
public double Mul(double a, double b) {
    return a * b;
}
public double Sub(double a, double b) {
    return a - b;
}
public double Add(double a, double b) {
    return a + b;
}

3.Calculator類的使用方法:

public static void main(String[] args) {
    System.out.print("輸入算式:");
    Scanner scanner = new Scanner(System.in);
    String express = scanner.nextLine();
    Calculator calculator = new Calculator(express);
    System.out.println(calculator.Calculated());
}

4.輸出結果:

輸入算式:(1+2)*6+1
19.0

圖形化設計

這個圖形化設計我就不多說了,簡要說一下就行了。

首先,我們設計一個圖形界面肯定需要下面這幾個步驟:

  1. 設置界面
  2. 判斷監聽器
  3. 進行合理輸入

一、設置界面

大家根據自己的個人喜好,隨意設計。我的大概界面如下圖:

在這裏插入圖片描述
程序中組件介紹:

  1. JLabel #標籤
  2. JTextField #文本框
  3. JButton #按鈕

界面大家就自己設置就行了,我就不再過多的贅餘了,代碼中有詳細的註釋,具體見代碼:

public class CalculatorFrame extends JFrame {
    JLabel resultLabel = new JLabel(); //結果存放處
    JTextField inputText = new JTextField("0"); //輸入框
    JButton clearEmpty = new JButton("CE"); //清除當前輸入按鈕
    JButton clear = new JButton("C"); //清除所有輸入按鈕
    JButton backSpace = new JButton("BackSpace"); //刪除按鈕
    JButton leftPer = new JButton("("); //左括號按鈕
    JButton rightPer = new JButton(")"); //右括號按鈕
    JButton divisor = new JButton("/"); //除號按鈕
    JButton multiply = new JButton("*"); //乘號按鈕
    JButton minus = new JButton("-"); //減號按鈕
    JButton plus = new JButton("+"); //加號按鈕
    JButton posNeg = new JButton("+/-"); //正負號按鈕
    JButton decimalPoint = new JButton("."); //小數點按鈕
    JButton radicalSign = new JButton("√"); //開根號按鈕
    JButton equal = new JButton("="); //等於號按鈕
    JButton[] button =  new JButton[10]; //0~9按鈕
    public static void main(String[] args) {
        new CalculatorFrame();
    }
    CalculatorFrame() {
        init();
        setLayout(null);
        setTitle("計算器");
        setResizable(false);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setBounds(400, 100, 365, 420);
        setVisible(true);
    }
    public void init() {
        //字體
        Font font = new Font("Courier new", Font.PLAIN, 20);

        //最終結果標籤
        resultLabel.setBounds(20, 20, 310, 25); //設置位置和寬高
        resultLabel.setFont(font); //添加字體樣式
        add(resultLabel); //添加到窗體

        //輸入框樣式
        LineBorder lineBorder = new LineBorder(Color.lightGray, 3, true);
        inputText.setBorder(lineBorder); //添加樣式
        inputText.setEditable(false); //文本框是否可編輯
        inputText.setBounds(20, 45, 310, 25); //設置位置和寬高
        inputText.setFont(font); //添加字體樣式
        add(inputText); //添加到窗體

        //CE按鈕樣式
        clearEmpty.setBounds(20, 90, 70, 35);
        clearEmpty.setFont(font);
        add(clearEmpty);

        //C按鈕樣式
        clear.setBounds(100, 90, 70, 35);
        clear.setFont(font);
        add(clear);

        //BackSpace按鈕樣式
        backSpace.setBounds(180, 90, 150, 35);
        backSpace.setFont(font);
        add(backSpace);

        //除號按鈕樣式
        divisor.setBounds(260, 135, 70, 35);
        divisor.setFont(font);
        add(divisor);

        //乘號按鈕樣式
        multiply.setBounds(260, 180, 70, 35);
        multiply.setFont(font);
        add(multiply);

        //減號按鈕樣式
        minus.setBounds(260, 225, 70, 35);
        minus.setFont(font);
        add(minus);

        //加號按鈕樣式
        plus.setBounds(260, 270, 70, 35);
        plus.setFont(font);
        add(plus);
        int i = 1;

        //0號按鈕樣式
        button[0] = new JButton("0");
        button[0].setBounds(20, 270, 70, 35);
        button[0].setFont(font);
        add(button[0]);

        //正負號按鈕樣式
        posNeg.setBounds(100, 270, 70, 35);
        posNeg.setFont(font);
        add(posNeg);

        //小數點按鈕樣式
        decimalPoint.setBounds(180, 270, 70, 35);
        decimalPoint.setFont(font);
        add(decimalPoint);

        //1~9按鈕樣式
        for (int j = 225; j >= 135; j -= 45) {
            for (int k = 20; k <= 180; k += 80) {
                button[i] = new JButton(Integer.toString(i));
                button[i].setBounds(k, j, 70, 35);
                button[i].setFont(font);
                add(button[i++]);
            }
        }

        //左括號按鈕樣式
        leftPer.setBounds(20, 315, 70, 35);
        leftPer.setFont(font);
        add(leftPer);

        //右括號按鈕樣式
        rightPer.setBounds(100, 315, 70, 35);
        rightPer.setFont(font);
        add(rightPer);

        //根號按鈕樣式
        radicalSign.setBounds(180, 315, 70, 35);
        radicalSign.setFont(font);
        add(radicalSign);

        //等於號按鈕樣式
        equal.setBounds(260, 315, 70, 35);
        equal.setFont(font);
        add(equal);
    }
}

二、判斷監聽器

監聽器

當用戶按下一個按鈕或者單擊某個菜單項時,這些動作就會激發一個相應的事件,該事件就會觸發事件源上註冊的事件監聽器(特殊的Java對象),事件監聽器調用相應的事件處理器(事件監聽器裏的實例方法)來做出相應的響應。

三種方法:

  1. 匿名內部類;
  2. 匿名外部類;
  3. 實現接口;

這裏使用第三種方法——自身類實現ActionListener接口,作爲事件監聽器:

public class CalculatorFrame extends JFrame implements ActionListener {
    JLabel resultLabel = new JLabel(); //結果存放處
    JTextField inputText = new JTextField("0"); //輸入框
    JButton clearEmpty = new JButton("CE"); //清除當前輸入按鈕
    JButton clear = new JButton("C"); //清除所有輸入按鈕
    JButton backSpace = new JButton("BackSpace"); //刪除按鈕
    JButton leftPer = new JButton("("); //左括號按鈕
    JButton rightPer = new JButton(")"); //右括號按鈕
    JButton divisor = new JButton("/"); //除號按鈕
    JButton multiply = new JButton("*"); //乘號按鈕
    JButton minus = new JButton("-"); //減號按鈕
    JButton plus = new JButton("+"); //加號按鈕
    JButton posNeg = new JButton("+/-"); //正負號按鈕
    JButton decimalPoint = new JButton("."); //小數點按鈕
    JButton radicalSign = new JButton("√"); //開根號按鈕
    JButton equal = new JButton("="); //等於號按鈕
    JButton[] button =  new JButton[10]; //0~9按鈕
    StringBuffer s1 = new StringBuffer("0"); //記錄運算數字,以及保留結果
    StringBuffer s2 = new StringBuffer(); //記錄運算數字,保留上一個輸入的數字或運算結果
    boolean operator = false; //判斷上次輸入的是否爲運算符
    boolean start = true; //標記運算開始或結束,保證一次運算之後,第二次進行運算時能同時清空顯示界面,即s1爲空
    public static void main(String[] args) {
        new CalculatorFrame();
    }
    CalculatorFrame() {
        init();
        setLayout(null);
        setTitle("計算器");
        setResizable(false);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setBounds(400, 100, 365, 420);
        setVisible(true);
    }
    public void init() {
        //字體
        Font font = new Font("Courier new", Font.PLAIN, 20);

        //最終結果標籤
        resultLabel.setBounds(20, 20, 310, 25); //設置位置和寬高
        resultLabel.setFont(font); //添加字體樣式
        add(resultLabel); //添加到窗體

        //輸入框樣式
        LineBorder lineBorder = new LineBorder(Color.lightGray, 3, true);
        inputText.setBorder(lineBorder); //添加樣式
        inputText.setEditable(false); //文本框是否可編輯
        inputText.setBounds(20, 45, 310, 25); //設置位置和寬高
        inputText.setFont(font); //添加字體樣式
        add(inputText); //添加到窗體

        //CE按鈕樣式
        clearEmpty.setBounds(20, 90, 70, 35);
        clearEmpty.setFont(font);
        add(clearEmpty);
        clearEmpty.addActionListener(this); //設置監聽器

        //C按鈕樣式
        clear.setBounds(100, 90, 70, 35);
        clear.setFont(font);
        add(clear);
        clear.addActionListener(this);

        //BackSpace按鈕樣式
        backSpace.setBounds(180, 90, 150, 35);
        backSpace.setFont(font);
        add(backSpace);
        backSpace.addActionListener(this);

        //除號按鈕樣式
        divisor.setBounds(260, 135, 70, 35);
        divisor.setFont(font);
        add(divisor);
        divisor.addActionListener(this);

        //乘號按鈕樣式
        multiply.setBounds(260, 180, 70, 35);
        multiply.setFont(font);
        add(multiply);
        multiply.addActionListener(this);

        //減號按鈕樣式
        minus.setBounds(260, 225, 70, 35);
        minus.setFont(font);
        add(minus);
        minus.addActionListener(this);

        //加號按鈕樣式
        plus.setBounds(260, 270, 70, 35);
        plus.setFont(font);
        add(plus);
        plus.addActionListener(this);
        int i = 1;

        //0號按鈕樣式
        button[0] = new JButton("0");
        button[0].setBounds(20, 270, 70, 35);
        button[0].setFont(font);
        add(button[0]);
        button[0].addActionListener(this);

        //正負號按鈕樣式
        posNeg.setBounds(100, 270, 70, 35);
        posNeg.setFont(font);
        add(posNeg);
        posNeg.addActionListener(this);

        //小數點按鈕樣式
        decimalPoint.setBounds(180, 270, 70, 35);
        decimalPoint.setFont(font);
        add(decimalPoint);
        decimalPoint.addActionListener(this);

        //1~9按鈕樣式
        for (int j = 225; j >= 135; j -= 45) {
            for (int k = 20; k <= 180; k += 80) {
                button[i] = new JButton(Integer.toString(i));
                button[i].setBounds(k, j, 70, 35);
                button[i].setFont(font);
                button[i].addActionListener(this);
                add(button[i++]);
            }
        }

        //左括號按鈕樣式
        leftPer.setBounds(20, 315, 70, 35);
        leftPer.setFont(font);
        add(leftPer);
        leftPer.addActionListener(this);

        //右括號按鈕樣式
        rightPer.setBounds(100, 315, 70, 35);
        rightPer.setFont(font);
        add(rightPer);
        rightPer.addActionListener(this);

        //根號按鈕樣式
        radicalSign.setBounds(180, 315, 70, 35);
        radicalSign.setFont(font);
        add(radicalSign);
        radicalSign.addActionListener(this);

        //等於號按鈕樣式
        equal.setBounds(260, 315, 70, 35);
        equal.setFont(font);
        add(equal);
        equal.addActionListener(this);
    }
    //監聽器
    public void actionPerformed(ActionEvent e) {
        //判斷是否爲按鈕0
        if (e.getSource().equals(button[0])) {
            
        }

        //判斷是否爲1~9按鈕
        for (int i = 1; i <= 9; i++) {
            if (e.getSource().equals(button[i])) {
                
            }
        }

        //判斷小數點
        if (e.getSource().equals(decimalPoint)) {
            
        }

        //判斷正負號
        if (e.getSource().equals(posNeg)) {
            
        }

        //判斷退格Backspace
        if (e.getSource().equals(backSpace)) {
            
        }

        //判斷歸零CE
        if (e.getSource().equals(clearEmpty)) {
            
        }

        //判斷清除C
        if (e.getSource().equals(clear)) {
            
        }

        //判斷加號
        if (e.getSource().equals(plus)) {
            
        }

        //判斷減號
        if (e.getSource().equals(minus)) {
            
        }

        //判斷乘號
        if (e.getSource().equals(multiply)) {
            
        }

        //判斷除號
        if (e.getSource().equals(divisor)) {
            
        }

        //判斷左括號
        if (e.getSource().equals(leftPer)) {
            
        }

        //判斷右括號
        if (e.getSource().equals(rightPer)) {
            
        }

        //判斷根號
        if (e.getSource().equals(radicalSign)) {
            
        }
        //判斷等號
        if (e.getSource().equals(equal)) {
            
        }
    }
}

三、進行合理輸入

(1).我們知道, 我們的算術表達式是不能隨意輸入的,要符合我們正常的算術表達式的邏輯形式。我們知道合法輸入應具備下面這些條件:

  1. 數字沒有前導0
  2. 小數點只能跟在數字後面且只能出現一次
  3. 左括號前面必須是運算符、左括號或者空
  4. 左括號後面不能直接跟運算符
  5. 右括號前面必須是數字或右括號
  6. 右括號後面不能直接跟數字
  7. 根號下面的結果必須是數字且大於等於0

我們知道,我們計算算術表達式的程序返回的結果有可能是Error!此時運算符、小數點、左右括號、等於號是不能用的。最後,我們可以得到:

public void actionPerformed(ActionEvent e) {
    //判斷是否爲按鈕0
    if (e.getSource().equals(button[0])) {
        if (!s1.toString().equals("0") && !s1.toString().equals("-0") && s1.charAt(s1.length() - 1) != ')') { //')'後面不能直接跟數字
            if (!start || s1.charAt(0) == 'E') { //如果是開始新一輪的運算或是有錯誤提示
                //s1清零,保證可以重新輸入數字
                s1.delete(0, s1.length());
                s2.delete(0, s2.length());
            }
            start = true;
            s1.append("0");
            operator = false;
        }
    }

    //判斷是否爲1~9按鈕
    for (int i = 1; i <= 9; i++) {
        if (e.getSource().equals(button[i])) {
            if (!start || s1.charAt(0) == 'E') { //如果是開始新一輪的運算或是有錯誤提示
                s1.delete(0, s1.length()); //清空
                s2.delete(0, s2.length());
            }
            start = true;
            if (s1.toString().equals("0") || s1.toString().equals("-0"))
                s1.deleteCharAt(s1.length() - 1); //去除開始的0
            if (s1.length() == 0 || s1.charAt(s1.length() - 1) != ')')
                s1.append(i); //')'後面不能直接跟數字
            operator = false; //輸入的不是運算符
        }
    }

    //判斷小數點
    if (e.getSource().equals(decimalPoint)) {
        if (!start || s1.charAt(0) == 'E') {
            s1.delete(0, s1.length());
            s1.append("0");
            s2.delete(0, s2.length());
        }
        start = true;
        if (Character.isDigit(s1.charAt(s1.length() - 1)) && s1.indexOf(".") == -1)
            s1.append("."); //如果前面是數字且沒出現過小數點
        operator = false; //輸入的不是運算符
    }

    //判斷正負號
    if (e.getSource().equals(posNeg)) {
        if (s1.charAt(0) == 'E') //如果是錯誤提示
            return ;
        if (!start) { //如果開始新的運算
            s1.delete(0, s1.length());
            s1.append("0");
            s2.delete(0, s2.length());
        }
        start = true;
        int i;
        for (i = s1.length() - 1; i > 0; --i) //從前往後找,直到遇到不是數字或小數點,在其添加負號
            if (!Character.isDigit(s1.charAt(i)) && s1.charAt(i) != '.')
                break;
        if (Character.isDigit(s1.charAt(i))) //如果都是數字
            s1.insert(i, '-');
        else  if (s1.charAt(i) != '-') //如果此處不是負號,添加負號
            s1.insert(i + 1, '-');
        else s1.deleteCharAt(i); //否則已經有負號,刪除
        operator = false; //輸入的不是運算符
    }

    //判斷退格Backspace
    if (e.getSource().equals(backSpace)) {
        start = true;
        s1.deleteCharAt(s1.length() - 1); //刪除最後一個
        if (s1.length() == 0)
            s1.append("0");
        operator = false;
    }

    //判斷歸零CE
    if (e.getSource().equals(clearEmpty)) {
        //清空當前輸入,即s1清零
        //start = true;
        s1.delete(0, s1.length()); //清空
        s1.append("0");
        operator = false;
    }

    //判斷清除C
    if (e.getSource().equals(clear)) {
        //清空所有,start標記設爲true
        start = true;
        s1.delete(0, s1.length());
        s2.delete(0, s2.length());
        s1.append("0");
        operator = false;
    }

    //判斷加號
    if (e.getSource().equals(plus)) {
        if (s1.charAt(0) == 'E') //如果是錯誤提示,無操作
            return ;
        if (!start)
            s2.delete(0, s2.length());
        //運算符前面不能是做括號和小數點,自動捨去
        while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
            s1.deleteCharAt(s1.length() - 1);
        }
        //如果上次是運算符,那麼就可以替換上次輸入的運算符
        if (operator && s2.length() > 0) {
            s2.deleteCharAt(s2.length() - 1);
            s2.append("+");
        }
        else if (s1.length() > 0) //如果s1還有數據
            s2.append(s1.toString() + "+");
        //s1清零,重新接收下一個數據
        s1.delete(0, s1.length());
        s1.append("0");
        start = true;
        operator = true;
    }

    //判斷減號
    if (e.getSource().equals(minus)) {
        if (s1.charAt(0) == 'E')
            return ;
        if (!start)
            s2.delete(0, s2.length());
        //運算符前面不能是做括號和小數點,自動捨去
        while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
            s1.deleteCharAt(s1.length() - 1);
        }
        //如果上次是運算符,那麼就可以替換上次輸入的運算符
        if (operator && s2.length() > 0) {
            s2.deleteCharAt(s2.length() - 1);
            s2.append("-");
        }
        else if (s1.length() > 0) //如果s1還有數據
            s2.append(s1.toString() + "-");
        s1.delete(0, s1.length());
        s1.append("0");
        start = true;
        operator = true;
    }

    //判斷乘號
    if (e.getSource().equals(multiply)) {
        if (s1.charAt(0) == 'E')
            return ;
        if (!start)
            s2.delete(0, s2.length());
        //運算符前面不能是做括號和小數點,自動捨去
        while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
            s1.deleteCharAt(s1.length() - 1);
        }
        //如果上次是運算符,那麼就可以替換上次輸入的運算符
        if (operator && s2.length() > 0) {
            s2.deleteCharAt(s2.length() - 1);
            s2.append("*");
        }
        else if (s1.length() > 0) //如果s1還有數據
            s2.append(s1.toString() + "*");
        s1.delete(0, s1.length());
        s1.append("0");
        start = true;
        operator = true;
    }

    //判斷除號
    if (e.getSource().equals(divisor)) {
        if (s1.charAt(0) == 'E')
            return ;
        if (!start)
            s2.delete(0, s2.length());
        //運算符前面不能是做括號和小數點,自動捨去
        while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
            s1.deleteCharAt(s1.length() - 1);
        }
        //如果上次是運算符,那麼就可以替換上次輸入的運算符
        if (operator && s2.length() > 0) {
            s2.deleteCharAt(s2.length() - 1);
            s2.append("/");
        }
        else if (s1.length() > 0) //如果s1還有數據
            s2.append(s1.toString() + "/");

        s1.delete(0, s1.length());
        s1.append("0");
        start = true;
        operator = true;
    }

    //判斷左括號
    if (e.getSource().equals(leftPer)) {
        if (!start || s1.charAt(0) == 'E') { //開始新一輪的運算或是錯誤信息
            s1.delete(0, s1.length()); //清空
            s1.append("0");
            s2.delete(0, s2.length());
        }
        start = true;
        //只能在初始狀態的時候或者左邊爲左括號的時候才能添加左括號
        if (s1.toString().equals("0") || s1.toString().equals("-0")) { //如果是初始狀態
            s1.deleteCharAt(s1.length() - 1);
            s1.append("(");
        }
        else if (s1.charAt(s1.length() - 1) == '(') //
            s1.append("(");
        operator = false;
    }

    //判斷右括號
    if (e.getSource().equals(rightPer)) {
        if (!start || s1.charAt(0) == 'E') {
            s1.delete(0, s1.length());
            s1.append("0");
            s2.delete(0, s2.length());
        }
        start = true;
        //前面必須是數字或右括號才能添加
        if (Character.isDigit(s1.charAt(s1.length() - 1)) || s1.charAt(s1.length() - 1) == ')')
            s1.append(")");
        operator = false;
    }

    //判斷根號
    if (e.getSource().equals(radicalSign)) {
        if (s1.charAt(0) == 'E')
            return ;
        start = false;
        //刪除無用的數據
        while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
            s1.deleteCharAt(s1.length() - 1);
        }
        if (s1.length() == 0)
            s1.append("0");
        String str = new Calculator(s1.toString()).Calculated();//先計算出s1中的表達式
        s2.delete(0, s2.length());
        s2.append(s1);
        s1.delete(0, s1.length());
        s2.insert(0, "√(");
        if (str.equals("Error!") || Double.parseDouble(str) < 0) //如果表達式有錯誤或結果爲負數
            s1.append("Error!");
        else {
            double s = Double.parseDouble(str);
            s1.append(Math.sqrt(s));
        }
        s2.append(")");
        operator = false;
    }

    //判斷等號
    if (e.getSource().equals(equal)) {
        if (s1.charAt(0) == 'E')
            return ;
        start = false;
        //刪除無用的數據
        while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
            s1.deleteCharAt(s1.length() - 1);
        }
        //如果s1爲空,補0
        if (s1.length() == 0)
            s1.append("0");
        String str = new Calculator(s2.append(s1.toString()).toString()).Calculated(); //計算
        if (!str.equals("Error!")) //不是錯誤信息
            s2.append( "=" + str);
        else s2.append("=");
        s1.delete(0, s1.length());
        s1.append(str);
        operator = false;
    }
    inputText.setText(s1.toString()); //添加到輸入框
    resultLabel.setText(s2.toString()); //添加到結果標籤
}

上面的程序實現了表達式的判定和計算,爲了讓計算器有更好的體驗,我們可以爲計算器加上圖形界面,下面的代碼主要就是實現了一個圖形界面,配合上面的代碼,就可以組成一個完美的計算器:

源碼

Calculator

這個是計算表達式的源碼,可直接運行。

package Calculator;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack;

public class Calculator {
    private String expression; //存儲表達式
    private Map<String, Integer> priority = new HashMap<>();
    /**
     * @構造方法
     * @param expression
     */
    public Calculator(String expression) {
        init();
        this.expression = expression;
    }
    /**
     * @初始化
     */
    public void init() {
        priority.put("/", 1);
        priority.put("*", 1);
        priority.put("-", 0);
        priority.put("+", 0);
        priority.put("(", -1);
        priority.put(")", -1);
        priority.put("#", -2);
    }
    /**
     * @表達式的合法性檢測
     * @return boolean
     */
    public boolean Check() {
        //首先在負號前面補0
        for (int i = 0; i < expression.length(); i++) {
            if (expression.charAt(i) == '-' && ((i > 0 && !Character.isDigit(expression.charAt(i - 1))) || i == 0)) {
                StringBuilder exp = new StringBuilder(expression);
                exp.insert(i, "0");
                expression = exp.toString();System.out.println(expression);
            }
        }
        //判斷括號是否匹配,'('加1,')'減1
        int backets = 0;
        //從左向右掃描
        for (int i = 0; i < expression.length(); i++) {
            switch (expression.charAt(i)) {
                case '(': ++backets; break; //'('加1
                case ')': --backets; //')'減1
                    if (backets < 0) //中間出現'('少於')'的情況
                        return false;
                    break;
                case '/':
                case '*':
                case '+':
                    if (i == 0) return false; // '/'、'*'、'+'不能出現首位
                    if (i > 0 && (expression.charAt(i - 1) == '/' || expression.charAt(i - 1) == '*' || expression.charAt(i - 1) == '+' || expression.charAt(i - 1) == '-' || expression.charAt(i - 1) == '('))
                        return false; //運算符不能連續出現且前面不能爲'('
                case '-':
                    if (i == expression.length() - 1) return false; //運算符不能出現在尾部
                    if (expression.charAt(i + 1) == '/' || expression.charAt(i + 1) == '*' || expression.charAt(i + 1) == '+' || expression.charAt(i + 1) == '-' || expression.charAt(i + 1) == ')')
                        return false; //運算符不能連續出現且後面不能爲')'
            }
        }
        if (backets != 0) //如果括號不匹配
            return false;
        return true;
    }

    /**
     * @中綴表達式轉換爲後綴表達式
     * @return Stack
     */
    public Stack<String> Conversion() {
        Stack<String> s1 = new Stack<>();
        Stack<String> s2 = new Stack<>();
        s1.push("#"); //將最低優先級的#符號放入S1棧,爲了方便統一後續操作
        for (int i = 0; i < expression.length(); i++) { //循環遍歷表達式
            switch (expression.charAt(i)) {
                case '(': s1.push("("); break; //讀取到左括號,直接壓入S1棧
                case ')': //若取出的字符是")",則將距離S1棧棧頂最近的"("之間的運算符,逐個出棧,依次送入S2棧,此時拋棄"("。
                    while (!s1.peek().equals("(")) {
                        s2.push(s1.pop());
                    }
                    s1.pop(); break;
                case '/':
                case '*':
                case '-':
                case '+':
                    /*
                    * 若取出的字符是運算符,則將該運算符與S1棧棧頂元素比較,
                    * 如果該運算符優先級(不包括括號運算符)大於S1棧棧頂運算符優先級,則將該運算符進S1棧,
                    * 否則,將S1棧的棧頂運算符彈出,送入S2棧中,直至S1棧棧頂運算符低於(不包括等於)該運算符優先級,
                    * 最後將該運算符送入S1棧。
                    * */
                    if (priority.get(expression.charAt(i) + "") > priority.get(s1.peek())) {
                        s1.push(expression.charAt(i) + "");
                    }
                    else {
                        while (!(priority.get(expression.charAt(i) + "") > priority.get(s1.peek()))) {
                            s2.push(s1.pop());
                        }
                        s1.push(expression.charAt(i) + "");
                    }
                    break;
                default:
                    //若取出的字符是操作數,則分析出完整的運算數
                    StringBuilder num = new StringBuilder();
                    while (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.') {
                        num.append(expression.charAt(i));
                        if (i + 1 < expression.length() && (Character.isDigit(expression.charAt(i + 1)) || expression.charAt(i + 1) == '.'))
                            ++i;
                        else break;
                    }
                    //該操作數直接送入S2棧
                    s2.push(num.toString()); break;
            }
        }
        //將S1棧內所有運算符(不包括"#"),逐個出棧,依次送入S2棧。
        while (!s1.peek().equals("#"))
            s2.push(s1.pop());
        //由於棧的特性,S2應做一下逆序處理
        Stack<String> stack = new Stack<>();
        while (!s2.empty()) {
            stack.push(s2.pop());
        }
        return stack; //返回S2的逆序棧
    }

    /**
     * @逆波蘭式的求值
     * @return String
     */
    public String Calculated() {
        if (!this.Check()) //合法性檢驗
            return "Error!"; //不合法返回"Error!"
        Stack<String> stack = Conversion(); //得到逆波蘭表達式
        Stack<Double> tmp = new Stack<>(); //聲明一個棧
        while (!stack.empty()) {
            String s = stack.pop(); //取出逆波蘭中的值
            if (Character.isDigit(s.charAt(0))) //如果是操作數
                tmp.push(Double.parseDouble(s)); //直接進入tmp棧
            else {//如果是運算符,取出兩個數進行計算
                double b = tmp.pop();
                double a = tmp.pop();
                switch (s) {
                    case "/":
                        if (b == 0.0) //如果除數爲0,報錯
                            return "Error!";
                        tmp.push(Div(a, b)); break;
                    case "*": tmp.push(Mul(a, b)); break;
                    case "-": tmp.push(Sub(a, b)); break;
                    case "+": tmp.push(Add(a, b)); break;
                }
            }
        }
        return tmp.pop().toString();
    }
    public double Div(double a, double b) {
        BigDecimal a1 = new BigDecimal(a);
        BigDecimal b1 = new BigDecimal(b);
        return a1.divide(b1, 5, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
    public double Mul(double a, double b) {
        return a * b;
    }
    public double Sub(double a, double b) {
        return a - b;
    }
    public double Add(double a, double b) {
        return a + b;
    }
    public static void main(String[] args) {
        System.out.print("輸入算式:");
        Scanner scanner = new Scanner(System.in);
        String express = scanner.nextLine();
        Calculator calculator = new Calculator(express);
        System.out.println(calculator.Calculated());
    }
}

CalculatorFrame

這個是圖形界面源碼,裏面需要用到Calculator類。

package Calculator;

import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class CalculatorFrame extends JFrame implements ActionListener {
    JLabel resultLabel = new JLabel(); //結果存放處
    JTextField inputText = new JTextField("0"); //輸入框
    JButton clearEmpty = new JButton("CE"); //清除當前輸入按鈕
    JButton clear = new JButton("C"); //清除所有輸入按鈕
    JButton backSpace = new JButton("BackSpace"); //刪除按鈕
    JButton leftPer = new JButton("("); //左括號按鈕
    JButton rightPer = new JButton(")"); //右括號按鈕
    JButton divisor = new JButton("/"); //除號按鈕
    JButton multiply = new JButton("*"); //乘號按鈕
    JButton minus = new JButton("-"); //減號按鈕
    JButton plus = new JButton("+"); //加號按鈕
    JButton posNeg = new JButton("+/-"); //正負號按鈕
    JButton decimalPoint = new JButton("."); //小數點按鈕
    JButton radicalSign = new JButton("√"); //開根號按鈕
    JButton equal = new JButton("="); //等於號按鈕
    JButton[] button =  new JButton[10]; //0~9按鈕
    StringBuffer s1 = new StringBuffer("0"); //記錄運算數字,以及保留結果
    StringBuffer s2 = new StringBuffer(); //記錄運算數字,保留上一個輸入的數字或運算結果
    boolean operator = false; //判斷上次輸入的是否爲運算符
    boolean start = true; //標記運算開始或結束,保證一次運算之後,第二次進行運算時能同時清空顯示界面,即s1爲空
    public static void main(String[] args) {
        new CalculatorFrame();
    }
    CalculatorFrame() {
        init();
        setLayout(null);
        setTitle("計算器");
        setResizable(false);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setBounds(400, 100, 365, 420);
        setVisible(true);
    }
    public void init() {
        //字體
        Font font = new Font("Courier new", Font.PLAIN, 20);

        //最終結果標籤
        resultLabel.setBounds(20, 20, 310, 25); //設置位置和寬高
        resultLabel.setFont(font); //添加字體樣式
        add(resultLabel); //添加到窗體

        //輸入框樣式
        LineBorder lineBorder = new LineBorder(Color.lightGray, 3, true);
        inputText.setBorder(lineBorder); //添加樣式
        inputText.setEditable(false); //文本框是否可編輯
        inputText.setBounds(20, 45, 310, 25); //設置位置和寬高
        inputText.setFont(font); //添加字體樣式
        add(inputText); //添加到窗體

        //CE按鈕樣式
        clearEmpty.setBounds(20, 90, 70, 35);
        clearEmpty.setFont(font);
        add(clearEmpty);
        clearEmpty.addActionListener(this); //設置監聽器

        //C按鈕樣式
        clear.setBounds(100, 90, 70, 35);
        clear.setFont(font);
        add(clear);
        clear.addActionListener(this);

        //BackSpace按鈕樣式
        backSpace.setBounds(180, 90, 150, 35);
        backSpace.setFont(font);
        add(backSpace);
        backSpace.addActionListener(this);

        //除號按鈕樣式
        divisor.setBounds(260, 135, 70, 35);
        divisor.setFont(font);
        add(divisor);
        divisor.addActionListener(this);

        //乘號按鈕樣式
        multiply.setBounds(260, 180, 70, 35);
        multiply.setFont(font);
        add(multiply);
        multiply.addActionListener(this);

        //減號按鈕樣式
        minus.setBounds(260, 225, 70, 35);
        minus.setFont(font);
        add(minus);
        minus.addActionListener(this);

        //加號按鈕樣式
        plus.setBounds(260, 270, 70, 35);
        plus.setFont(font);
        add(plus);
        plus.addActionListener(this);
        int i = 1;

        //0號按鈕樣式
        button[0] = new JButton("0");
        button[0].setBounds(20, 270, 70, 35);
        button[0].setFont(font);
        add(button[0]);
        button[0].addActionListener(this);

        //正負號按鈕樣式
        posNeg.setBounds(100, 270, 70, 35);
        posNeg.setFont(font);
        add(posNeg);
        posNeg.addActionListener(this);

        //小數點按鈕樣式
        decimalPoint.setBounds(180, 270, 70, 35);
        decimalPoint.setFont(font);
        add(decimalPoint);
        decimalPoint.addActionListener(this);

        //1~9按鈕樣式
        for (int j = 225; j >= 135; j -= 45) {
            for (int k = 20; k <= 180; k += 80) {
                button[i] = new JButton(Integer.toString(i));
                button[i].setBounds(k, j, 70, 35);
                button[i].setFont(font);
                button[i].addActionListener(this);
                add(button[i++]);
            }
        }

        //左括號按鈕樣式
        leftPer.setBounds(20, 315, 70, 35);
        leftPer.setFont(font);
        add(leftPer);
        leftPer.addActionListener(this);

        //右括號按鈕樣式
        rightPer.setBounds(100, 315, 70, 35);
        rightPer.setFont(font);
        add(rightPer);
        rightPer.addActionListener(this);

        //根號按鈕樣式
        radicalSign.setBounds(180, 315, 70, 35);
        radicalSign.setFont(font);
        add(radicalSign);
        radicalSign.addActionListener(this);

        //等於號按鈕樣式
        equal.setBounds(260, 315, 70, 35);
        equal.setFont(font);
        add(equal);
        equal.addActionListener(this);
    }
    //監聽器
    public void actionPerformed(ActionEvent e) {
        //判斷是否爲按鈕0
        if (e.getSource().equals(button[0])) {
            if (!s1.toString().equals("0") && !s1.toString().equals("-0") && s1.charAt(s1.length() - 1) != ')') { //')'後面不能直接跟數字
                if (!start || s1.charAt(0) == 'E') { //如果是開始新一輪的運算或是有錯誤提示
                    //s1清零,保證可以重新輸入數字
                    s1.delete(0, s1.length());
                    s2.delete(0, s2.length());
                }
                start = true;
                s1.append("0");
                operator = false;
            }
        }

        //判斷是否爲1~9按鈕
        for (int i = 1; i <= 9; i++) {
            if (e.getSource().equals(button[i])) {
                if (!start || s1.charAt(0) == 'E') { //如果是開始新一輪的運算或是有錯誤提示
                    s1.delete(0, s1.length()); //清空
                    s2.delete(0, s2.length());
                }
                start = true;
                if (s1.toString().equals("0") || s1.toString().equals("-0"))
                    s1.deleteCharAt(s1.length() - 1); //去除開始的0
                if (s1.length() == 0 || s1.charAt(s1.length() - 1) != ')')
                    s1.append(i); //')'後面不能直接跟數字
                operator = false; //輸入的不是運算符
            }
        }

        //判斷小數點
        if (e.getSource().equals(decimalPoint)) {
            if (!start || s1.charAt(0) == 'E') {
                s1.delete(0, s1.length());
                s1.append("0");
                s2.delete(0, s2.length());
            }
            start = true;
            if (Character.isDigit(s1.charAt(s1.length() - 1)) && s1.indexOf(".") == -1)
                s1.append("."); //如果前面是數字且沒出現過小數點
            operator = false; //輸入的不是運算符
        }

        //判斷正負號
        if (e.getSource().equals(posNeg)) {
            if (s1.charAt(0) == 'E') //如果是錯誤提示
                return ;
            if (!start) { //如果開始新的運算
                s1.delete(0, s1.length());
                s1.append("0");
                s2.delete(0, s2.length());
            }
            start = true;
            int i;
            for (i = s1.length() - 1; i > 0; --i) //從前往後找,直到遇到不是數字或小數點,在其添加負號
                if (!Character.isDigit(s1.charAt(i)) && s1.charAt(i) != '.')
                    break;
            if (Character.isDigit(s1.charAt(i))) //如果都是數字
                s1.insert(i, '-');
            else  if (s1.charAt(i) != '-') //如果此處不是負號,添加負號
                s1.insert(i + 1, '-');
            else s1.deleteCharAt(i); //否則已經有負號,刪除
            operator = false; //輸入的不是運算符
        }

        //判斷退格Backspace
        if (e.getSource().equals(backSpace)) {
            //start = true;
            s1.deleteCharAt(s1.length() - 1); //刪除最後一個
            if (s1.length() == 0)
                s1.append("0");
            operator = false;
        }

        //判斷歸零CE
        if (e.getSource().equals(clearEmpty)) {
            //清空當前輸入,即s1清零
            //start = true;
            s1.delete(0, s1.length()); //清空
            s1.append("0");
            operator = false;
        }

        //判斷清除C
        if (e.getSource().equals(clear)) {
            //清空所有,start標記設爲true
            start = true;
            s1.delete(0, s1.length());
            s2.delete(0, s2.length());
            s1.append("0");
            operator = false;
        }

        //判斷加號
        if (e.getSource().equals(plus)) {
            if (s1.charAt(0) == 'E') //如果是錯誤提示,無操作
                return ;
            if (!start)
                s2.delete(0, s2.length());
            //運算符前面不能是做括號和小數點,自動捨去
            while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
                s1.deleteCharAt(s1.length() - 1);
            }
            //如果上次是運算符,那麼就可以替換上次輸入的運算符
            if (operator && s2.length() > 0) {
                s2.deleteCharAt(s2.length() - 1);
                s2.append("+");
            }
            else if (s1.length() > 0) //如果s1還有數據
                s2.append(s1.toString() + "+");
            //s1清零,重新接收下一個數據
            s1.delete(0, s1.length());
            s1.append("0");
            start = true;
            operator = true;
        }

        //判斷減號
        if (e.getSource().equals(minus)) {
            if (s1.charAt(0) == 'E')
                return ;
            if (!start)
                s2.delete(0, s2.length());
            //運算符前面不能是做括號和小數點,自動捨去
            while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
                s1.deleteCharAt(s1.length() - 1);
            }
            //如果上次是運算符,那麼就可以替換上次輸入的運算符
            if (operator && s2.length() > 0) {
                s2.deleteCharAt(s2.length() - 1);
                s2.append("-");
            }
            else if (s1.length() > 0) //如果s1還有數據
                s2.append(s1.toString() + "-");
            s1.delete(0, s1.length());
            s1.append("0");
            start = true;
            operator = true;
        }

        //判斷乘號
        if (e.getSource().equals(multiply)) {
            if (s1.charAt(0) == 'E')
                return ;
            if (!start)
                s2.delete(0, s2.length());
            //運算符前面不能是做括號和小數點,自動捨去
            while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
                s1.deleteCharAt(s1.length() - 1);
            }
            //如果上次是運算符,那麼就可以替換上次輸入的運算符
            if (operator && s2.length() > 0) {
                s2.deleteCharAt(s2.length() - 1);
                s2.append("*");
            }
            else if (s1.length() > 0) //如果s1還有數據
                s2.append(s1.toString() + "*");
            s1.delete(0, s1.length());
            s1.append("0");
            start = true;
            operator = true;
        }

        //判斷除號
        if (e.getSource().equals(divisor)) {
            if (s1.charAt(0) == 'E')
                return ;
            if (!start)
                s2.delete(0, s2.length());
            //運算符前面不能是做括號和小數點,自動捨去
            while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
                s1.deleteCharAt(s1.length() - 1);
            }
            //如果上次是運算符,那麼就可以替換上次輸入的運算符
            if (operator && s2.length() > 0) {
                s2.deleteCharAt(s2.length() - 1);
                s2.append("/");
            }
            else if (s1.length() > 0) //如果s1還有數據
                s2.append(s1.toString() + "/");

            s1.delete(0, s1.length());
            s1.append("0");
            start = true;
            operator = true;
        }

        //判斷左括號
        if (e.getSource().equals(leftPer)) {
            if (!start || s1.charAt(0) == 'E') { //開始新一輪的運算或是錯誤信息
                s1.delete(0, s1.length()); //清空
                s1.append("0");
                s2.delete(0, s2.length());
            }
            start = true;
            //只能在初始狀態的時候或者左邊爲左括號的時候才能添加左括號
            if (s1.toString().equals("0") || s1.toString().equals("-0")) { //如果是初始狀態
                s1.deleteCharAt(s1.length() - 1);
                s1.append("(");
            }
            else if (s1.charAt(s1.length() - 1) == '(') //
                s1.append("(");
            operator = false;
        }

        //判斷右括號
        if (e.getSource().equals(rightPer)) {
            if (!start || s1.charAt(0) == 'E') {
                s1.delete(0, s1.length());
                s1.append("0");
                s2.delete(0, s2.length());
            }
            start = true;
            //前面必須是數字或右括號才能添加
            if (Character.isDigit(s1.charAt(s1.length() - 1)) || s1.charAt(s1.length() - 1) == ')')
                s1.append(")");
            operator = false;
        }

        //判斷根號
        if (e.getSource().equals(radicalSign)) {
            if (s1.charAt(0) == 'E')
                return ;
            start = false;
            //刪除無用的數據
            while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
                s1.deleteCharAt(s1.length() - 1);
            }
            if (s1.length() == 0)
                s1.append("0");
            String str = new Calculator(s1.toString()).Calculated();//先計算出s1中的表達式
            s2.delete(0, s2.length());
            s2.append(s1);
            s1.delete(0, s1.length());
            s2.insert(0, "√(");
            if (str.equals("Error!") || Double.parseDouble(str) < 0) //如果表達式有錯誤或結果爲負數
                s1.append("Error!");
            else {
                double s = Double.parseDouble(str);
                s1.append(Math.sqrt(s));
            }
            s2.append(")");
            operator = false;
        }

        //判斷等號
        if (e.getSource().equals(equal)) {
            if (s1.charAt(0) == 'E')
                return ;
            start = false;
            //刪除無用的數據
            while (s1.length() > 0 && (s1.charAt(s1.length() - 1) == '(' || s1.charAt(s1.length() - 1) == '.')) {
                s1.deleteCharAt(s1.length() - 1);
            }
            //如果s1爲空,補0
            if (s1.length() == 0)
                s1.append("0");
            String str = new Calculator(s2.append(s1.toString()).toString()).Calculated(); //計算
            if (!str.equals("Error!")) //不是錯誤信息
                s2.append( "=" + str);
            else s2.append("=");
            s1.delete(0, s1.length());
            s1.append(str);
            operator = false;
        }
        inputText.setText(s1.toString()); //添加到輸入框
        resultLabel.setText(s2.toString()); //添加到結果標籤
    }
}

END

至此,帶有圖形化的計算器就已經完成了,代碼可以查看GitHub倉庫

由於本人是一個小白,有錯誤的地方可以在下方評論區留言或私信我,我們可以相互討論~~.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章