計算機 是 GUI 程式設計的經典入門專案,涵蓋事件驅動模型、佈局管理、狀態管理等核心概念。本文使用 Java Swing 實作一個支援加減乘除、小數點、退位、平方根等功能的桌面計算機。
┌─────────────────────┐ │ 顯示區域 (JTextField) │ ← 右對齊,唯讀 ├─────────────────────┤ │ 7 │ 8 │ 9 │ ÷ │ ├─────┼─────┼─────┼─────┤ │ 4 │ 5 │ 6 │ × │ ├─────┼─────┼─────┼─────┤ │ 1 │ 2 │ 3 │ - │ ├─────┼─────┼─────┼─────┤ │ 0 │ . │ ← │ + │ ├─────┼─────┼─────┼─────┤ │ C │ √ │ = │ └─────────────────────┘
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Calculator extends JFrame implements ActionListener {
private JTextField display;
private JButton[] numberButtons = new JButton[10];
private JButton[] operatorButtons = new JButton[4];
private JButton btnDot, btnEqual, btnClear, btnBack, btnSqrt;
private JPanel panel;
// 運算狀態
private double operand1 = 0;
private double operand2 = 0;
private int operator = -1; // 0:+, 1:-, 2:*, 3:/
private boolean isNewInput = true;
public Calculator() {
super("Java 簡易計算機");
setSize(320, 420);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false);
initComponents();
setVisible(true);
}
private void initComponents() {
// 顯示欄位
display = new JTextField("0");
display.setFont(new Font("Consolas", Font.BOLD, 28));
display.setHorizontalAlignment(JTextField.RIGHT);
display.setEditable(false);
display.setBackground(Color.WHITE);
display.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
add(display, BorderLayout.NORTH);
// 按鈕面板 - GridLayout 4行4列
panel = new JPanel(new GridLayout(5, 4, 5, 5));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// 數字鍵 0-9
for (int i = 0; i < 10; i++) {
numberButtons[i] = new JButton(String.valueOf(i));
numberButtons[i].setFont(new Font("微軟正黑體", Font.BOLD, 20));
numberButtons[i].addActionListener(this);
}
// 運算子鍵
String[] ops = {"+", "-", "×", "÷"};
for (int i = 0; i < 4; i++) {
operatorButtons[i] = new JButton(ops[i]);
operatorButtons[i].setFont(new Font("微軟正黑體", Font.BOLD, 20));
operatorButtons[i].setBackground(new Color(240, 240, 240));
operatorButtons[i].addActionListener(this);
}
// 功能鍵
btnDot = createFuncButton(".");
btnEqual = createFuncButton("=");
btnClear = createFuncButton("C");
btnBack = createFuncButton("←");
btnSqrt = createFuncButton("√");
// 佈局排列 (按視覺順序加入 panel)
// 第1行: 7 8 9 ÷
addButtons(7, 8, 9, 3);
// 第2行: 4 5 6 ×
addButtons(4, 5, 6, 2);
// 第3行: 1 2 3 -
addButtons(1, 2, 3, 1);
// 第4行: 0 . ← +
addButtons(0, -1, -2, 0); // -1=dot, -2=back
// 第5行: C √ = (佔兩格)
panel.add(btnClear);
panel.add(btnSqrt);
panel.add(btnEqual);
panel.add(new JLabel()); // 佔位
add(panel, BorderLayout.CENTER);
}
private JButton createFuncButton(String text) {
JButton btn = new JButton(text);
btn.setFont(new Font("微軟正黑體", Font.BOLD, 20));
btn.setBackground(new Color(220, 230, 241));
btn.addActionListener(this);
return btn;
}
private void addButtons(int n1, int n2, int n3, int opIndex) {
panel.add(numberButtons[n1]);
if (n2 == -1) panel.add(btnDot);
else if (n2 == -2) panel.add(btnBack);
else panel.add(numberButtons[n2]);
if (n3 == -1) panel.add(btnDot);
else if (n3 == -2) panel.add(btnBack);
else panel.add(numberButtons[n3]);
panel.add(operatorButtons[opIndex]);
}
@Override
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
// 數字鍵處理
for (int i = 0; i < 10; i++) {
if (src == numberButtons[i]) {
handleNumberInput(i);
return;
}
}
// 小數點
if (src == btnDot) {
handleDotInput();
return;
}
// 退位鍵
if (src == btnBack) {
handleBackspace();
return;
}
// 清除鍵
if (src == btnClear) {
clearAll();
return;
}
// 平方根
if (src == btnSqrt) {
handleSqrt();
return;
}
// 運算子鍵
for (int i = 0; i < 4; i++) {
if (src == operatorButtons[i]) {
handleOperator(i);
return;
}
}
// 等號鍵
if (src == btnEqual) {
handleEqual();
}
}
private void handleNumberInput(int num) {
String current = display.getText();
if (isNewInput || current.equals("0") || current.equals("錯誤")) {
display.setText(String.valueOf(num));
isNewInput = false;
} else {
display.setText(current + num);
}
}
private void handleDotInput() {
String current = display.getText();
if (isNewInput) {
display.setText("0.");
isNewInput = false;
} else if (!current.contains(".")) {
display.setText(current + ".");
}
}
private void handleBackspace() {
String current = display.getText();
if (current.length() > 1 && !current.equals("錯誤")) {
display.setText(current.substring(0, current.length() - 1));
} else {
display.setText("0");
isNewInput = true;
}
}
private void clearAll() {
display.setText("0");
operand1 = operand2 = 0;
operator = -1;
isNewInput = true;
}
private void handleSqrt() {
try {
double val = Double.parseDouble(display.getText());
if (val < 0) throw new ArithmeticException("負數無平方根");
display.setText(formatResult(Math.sqrt(val)));
isNewInput = true;
} catch (NumberFormatException ex) {
display.setText("錯誤");
}
}
private void handleOperator(int op) {
try {
double currentVal = Double.parseDouble(display.getText());
if (operator != -1 && !isNewInput) {
// 連續運算:先計算前一個運算
handleEqual();
currentVal = Double.parseDouble(display.getText());
}
operand1 = currentVal;
operator = op;
isNewInput = true;
} catch (NumberFormatException ex) {
display.setText("錯誤");
}
}
private void handleEqual() {
if (operator == -1) return; // 沒有待執行運算
try {
operand2 = Double.parseDouble(display.getText());
double result = 0;
switch (operator) {
case 0: result = operand1 + operand2; break;
case 1: result = operand1 - operand2; break;
case 2: result = operand1 * operand2; break;
case 3:
if (operand2 == 0) {
display.setText("錯誤:除以零");
return;
}
result = operand1 / operand2;
break;
}
display.setText(formatResult(result));
operator = -1;
isNewInput = true;
} catch (NumberFormatException ex) {
display.setText("錯誤");
}
}
private String formatResult(double value) {
// 整數顯示為無小數點,浮點數保留合理精度
if (value == (long) value) {
return String.format("%d", (long) value);
} else {
return String.format("%.10f", value).replaceAll("0*$", "").replaceAll("\\.$", "");
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Calculator());
}
}
計算機核心是一個有限狀態機,關鍵狀態變數:
operand1:第一運算元(暫存結果)operator:待執行運算子(-1 表示無)isNewInput:標記下一個輸入是否為新數字起始使用者輸入 3 + 4 × 5 = 時,採用即時運算邏輯(非科學計算機的優先級邏輯):
operand1=3, operator=+operand1=7, operator=×JList 或 JTextArea 記錄運算歷程KeyListener 對應數字鍵盤輸入