提供的程式碼是一個基於 java.applet.Applet 與 AWT/Swing 混用的計算機,**Applet 技術自 Java 9 起被標記移除,Java 11 完全移除**,現代瀏覽器均不支援。運行時出現的警告:
無法讀取 AppletViewer 的屬性檔案:C:\Users\Administrator.hotjava\properties 使用預設值。
僅為 AppletViewer 設定檔缺失,**非程式邏輯錯誤**,程式實際可在 AppletViewer 中運行。
| 問題 | 現狀 | 建議修正 |
|---|---|---|
| 技術棧過時 | Applet + AWT + Swing 混用 | 遷移至 JavaFX / Swing 桌面應用 / Web (Spring Boot + Vue/React) |
| 事件處理冗長 | 20+ 個獨立 pressX() 方法 + 巨大 if-else 鏈 | 統一事件處理器、命令模式、Lambda 表達式 |
| 狀態管理分散 | str, sign, num, change 分散欄位 | 封裝 CalculatorModel 類別、狀態機模式 |
| 數值精度風險 | Double.parseDouble + 直接運算 | 使用 BigDecimal 處理金融/精確計算 |
| UI 佈局硬編碼 | GridLayout + setBounds 混用 | 使用 BorderLayout + GridBagLayout / JavaFX FXML |
| 錯誤處理缺失 | 除零、溢位、非數字輸入無處理 | try-catch、輸入驗證、使用者友善提示 |
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.function.BinaryOperator;
public class ModernCalculator extends JFrame {
private JTextField display;
private BigDecimal operand1 = null;
private BinaryOperator<BigDecimal> pendingOp = null;
private boolean startNewInput = true;
public ModernCalculator() {
setTitle("現代計算機");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
// 顯示器
display = new JTextField("0");
display.setEditable(false);
display.setHorizontalAlignment(JTextField.RIGHT);
display.setFont(new Font("Monospaced", Font.PLAIN, 24));
add(display, BorderLayout.NORTH);
// 按鈕面板
JPanel buttonPanel = new JPanel(new GridLayout(5, 4, 5, 5));
String[] buttons = {
"7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+",
"C", "⌫", "%", "±"
};
for (String text : buttons) {
JButton btn = new JButton(text);
btn.setFont(new Font("SansSerif", Font.BOLD, 18));
btn.addActionListener(this::handleButton);
buttonPanel.add(btn);
}
add(buttonPanel, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setResizable(false);
}
private void handleButton(ActionEvent e) {
String cmd = e.getActionCommand();
switch (cmd) {
case "0": case "1": case "2": case "3": case "4":
case "5": case "6": case "7": case "8": case "9":
inputDigit(cmd); break;
case ".": inputDecimal(); break;
case "+": case "-": case "*": case "/":
inputOperator(cmd); break;
case "=": calculateResult(); break;
case "C": clearAll(); break;
case "⌫": backspace(); break;
case "%": percent(); break;
case "±": toggleSign(); break;
}
}
private void inputDigit(String digit) {
if (startNewInput) {
display.setText(digit);
startNewInput = false;
} else {
if (!"0".equals(display.getText()))
display.setText(display.getText() + digit);
}
}
private void inputDecimal() {
if (startNewInput) { display.setText("0."); startNewInput = false; }
else if (!display.getText().contains(".")) display.setText(display.getText() + ".");
}
private void inputOperator(String op) {
if (operand1 != null && !startNewInput) calculateResult();
operand1 = new BigDecimal(display.getText());
pendingOp = switch (op) {
case "+" -> BigDecimal::add;
case "-" -> BigDecimal::subtract;
case "*" -> BigDecimal::multiply;
case "/" -> (a, b) -> a.divide(b, 10, RoundingMode.HALF_UP);
default -> null;
};
startNewInput = true;
}
private void calculateResult() {
if (operand1 == null || pendingOp == null) return;
BigDecimal operand2 = new BigDecimal(display.getText());
try {
BigDecimal result = pendingOp.apply(operand1, operand2);
display.setText(stripTrailingZeros(result));
} catch (ArithmeticException ex) {
display.setText("Error");
}
operand1 = null;
pendingOp = null;
startNewInput = true;
}
private String stripTrailingZeros(BigDecimal bd) {
return bd.stripTrailingZeros().toPlainString();
}
private void clearAll() { display.setText("0"); operand1 = null; pendingOp = null; startNewInput = true; }
private void backspace() { if (!startNewInput && display.getText().length() > 1) display.setText(display.getText().substring(0, display.getText().length()-1)); else display.setText("0"); }
private void percent() { if (!startNewInput) { BigDecimal v = new BigDecimal(display.getText()).divide(BigDecimal.valueOf(100)); display.setText(stripTrailingZeros(v)); } }
private void toggleSign() { if (!startNewInput) { BigDecimal v = new BigDecimal(display.getText()).negate(); display.setText(stripTrailingZeros(v)); } }
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ModernCalculator().setVisible(true));
}
}
handleButton 透過 ActionCommand 分派,消除 20+ 個獨立方法。double 浮點誤差(如 0.1+0.2=0.30000000000000004)。operand1, pendingOp, startNewInput 清楚定義計算機狀態。BinaryOperator<BigDecimal> + Lambda 取代巨大 switch。JFrame,main 啟動,無需瀏覽器/AppletViewer。SwingUtilities.invokeLater 確保執行緒安全。| 目標平台 | 技術棧 | 學習曲線 | 適用場景 |
|---|---|---|---|
| 桌面應用 | Swing (如上) / JavaFX (FXML + CSS) | 低/中 | 內部工具、離線應用、嵌入式介面 |
| Web 應用 | Spring Boot + Vue 3 / React + TypeScript | 中高 | 跨平台、協作、雲端部署、手機適配 |
| 行動應用 | Android (Kotlin/Jetpack Compose) / Flutter / React Native | 中高 | 原生體驗、離線優先、應用商店分發 |
原程式碼雖能在 AppletViewer 運行,但**技術棧已淘汰、架構不佳、維護困難**。建議直接重寫為現代 Swing/JavaFX 桌面應用或 Web 應用。上述重構範例展示了:事件統一分派、精確數值運算、狀態封裝、函數式策略模式等專業開發實踐,可作為學習遷移的起點。