1. 为什么选择Java开发计算器?
作为一名从C++转Java的老程序员,我依然记得2008年第一次用Java写计算器时的震撼。当时最让我惊讶的是Java的Swing组件竟然只需要几行代码就能创建出带界面的计算器,而不用像C++那样处理复杂的Windows API。时至今日,Java仍然是教学领域最受欢迎的入门语言之一,根据2023年JetBrains开发者生态调查报告,Java在全球编程语言使用率中稳居前五。
初学者选择Java开发计算器至少有三大优势:
- 语法严谨但不过于复杂,适合建立编程思维
- 丰富的标准库(如java.math包)直接支持高精度计算
- 可视化开发友好(Swing/JavaFX)
注意:虽然Python等语言写计算器更简单,但Java强类型的特点能帮助初学者养成声明变量类型的好习惯,这对后续学习数据结构等课程尤为重要。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境准备
2.1 JDK安装与验证
推荐使用OpenJDK 17 LTS版本(当前长期支持版),安装后需要检查:
bash复制java -version
# 应输出类似:openjdk version "17.0.8" 2023-07-18
2.2 IDE选择
- IntelliJ IDEA(社区版免费):智能代码补全对新手友好
- Eclipse:经典Java IDE,内存占用更小
- VS Code + Java扩展包:轻量级选择
避坑提示:遇到"源发行版17需要目标发行版17"错误时,需在IDE设置中同步以下配置:
- Project Structure → Project SDK
- Settings → Build → Java Compiler → Target bytecode version
3. 控制台版计算器实现
3.1 基础四则运算
java复制import java.util.Scanner;
public class BasicCalculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("输入第一个数字: ");
double num1 = scanner.nextDouble();
System.out.print("输入运算符(+-*/): ");
char operator = scanner.next().charAt(0);
System.out.print("输入第二个数字: ");
double num2 = scanner.nextDouble();
double result;
switch (operator) {
case '+': result = num1 + num2; break;
case '-': result = num1 - num2; break;
case '*': result = num1 * num2; break;
case '/':
if (num2 == 0) throw new ArithmeticException("除数不能为零");
result = num1 / num2;
break;
default: throw new IllegalArgumentException("无效运算符");
}
System.out.printf("结果: %.2f %c %.2f = %.2f", num1, operator, num2, result);
}
}
3.2 异常处理增强版
增加输入校验和异常捕获:
java复制try {
// ...原有代码...
} catch (InputMismatchException e) {
System.err.println("错误:请输入有效数字");
} catch (ArithmeticException e) {
System.err.println("数学错误:" + e.getMessage());
} catch (Exception e) {
System.err.println("未知错误:" + e.toString());
} finally {
scanner.close(); // 重要:释放资源
}
4. Swing图形界面计算器
4.1 基础界面搭建
java复制import javax.swing.*;
import java.awt.*;
public class SwingCalculator extends JFrame {
private final JTextField display = new JTextField("0");
public SwingCalculator() {
setTitle("Java计算器");
setSize(300, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
// 显示区域配置
display.setFont(new Font("Arial", Font.BOLD, 32));
display.setHorizontalAlignment(JTextField.RIGHT);
display.setEditable(false);
add(display, BorderLayout.NORTH);
// 按钮面板
JPanel buttonPanel = new JPanel(new GridLayout(4, 4, 5, 5));
String[] buttons = {"7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"C", "0", "=", "+"};
for (String text : buttons) {
JButton btn = new JButton(text);
btn.setFont(new Font("Arial", Font.PLAIN, 24));
buttonPanel.add(btn);
}
add(buttonPanel, BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new SwingCalculator().setVisible(true);
});
}
}
4.2 事件处理逻辑
扩展按钮功能实现:
java复制// 在构造函数中添加:
private double currentValue = 0;
private String pendingOp = "";
for (String text : buttons) {
JButton btn = new JButton(text);
btn.addActionListener(e -> {
String cmd = e.getActionCommand();
switch (cmd) {
case "C":
display.setText("0");
currentValue = 0;
pendingOp = "";
break;
case "=":
if (!pendingOp.isEmpty()) {
calculate(Double.parseDouble(display.getText()));
pendingOp = "";
}
break;
case "+": case "-": case "*": case "/":
if (!pendingOp.isEmpty()) {
calculate(Double.parseDouble(display.getText()));
} else {
currentValue = Double.parseDouble(display.getText());
}
pendingOp = cmd;
display.setText("0");
break;
default: // 数字处理
String curr = display.getText();
display.setText("0".equals(curr) ? cmd : curr + cmd);
}
});
// ...原有代码...
}
private void calculate(double secondValue) {
switch (pendingOp) {
case "+": currentValue += secondValue; break;
case "-": currentValue -= secondValue; break;
case "*": currentValue *= secondValue; break;
case "/":
if (secondValue == 0) {
JOptionPane.showMessageDialog(this, "除数不能为零", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
currentValue /= secondValue;
break;
}
display.setText(String.valueOf(currentValue));
}
5. 高级功能扩展
5.1 使用BigDecimal解决浮点精度问题
修改计算逻辑以避免0.1+0.2≠0.3等问题:
java复制import java.math.BigDecimal;
import java.math.RoundingMode;
private void calculate(String secondValue) {
BigDecimal a = new BigDecimal(currentValue);
BigDecimal b = new BigDecimal(secondValue);
switch (pendingOp) {
case "+": a = a.add(b); break;
case "-": a = a.subtract(b); break;
case "*": a = a.multiply(b); break;
case "/":
if (b.compareTo(BigDecimal.ZERO) == 0) {
// ...错误处理...
return;
}
a = a.divide(b, 10, RoundingMode.HALF_UP); // 保留10位小数
break;
}
currentValue = a.stripTrailingZeros().toString(); // 去除末尾多余的零
display.setText(currentValue);
}
5.2 添加历史记录功能
在界面右侧增加历史记录面板:
java复制// 成员变量
private JTextArea history = new JTextArea(10, 15);
// 在构造函数中
history.setEditable(false);
history.setFont(new Font("Consolas", Font.PLAIN, 12));
add(new JScrollPane(history), BorderLayout.EAST);
// 在calculate方法最后添加:
String record = String.format("%s %s %s = %s\n",
currentValue, pendingOp, secondValue, display.getText());
history.append(record);
6. 项目打包与分发
6.1 生成可执行JAR
在IntelliJ IDEA中:
- File → Project Structure → Artifacts
- 添加JAR → From modules with dependencies
- 选择主类(Main Class)
- Build → Build Artifacts
6.2 使用jpackage创建原生安装包(JDK14+)
bash复制jpackage --name MyCalculator --input target/ --main-jar calculator.jar --main-class com.example.Main
实用技巧:遇到"Lombok不支持当前编译器"警告时,在IDEA中安装Lombok插件并启用注解处理:
Settings → Build → Compiler → Annotation Processors → Enable annotation processing
7. 代码优化建议
-
使用状态模式:将加减乘除操作抽象为不同状态类,避免switch-case的无限扩展
-
引入单元测试:对核心计算逻辑编写JUnit测试
java复制@Test
void testAddition() {
Calculator calc = new Calculator();
assertEquals("5.0", calc.calculate("2+3"));
}
- 国际化支持:使用ResourceBundle实现多语言界面
java复制ResourceBundle bundle = ResourceBundle.getBundle("Messages", Locale.getDefault());
JButton btn = new JButton(bundle.getString("button.clear"));
- 换肤功能:通过UIManager切换外观
java复制UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
在实现过程中我发现一个有趣的现象:很多初学者会忽略事件队列(EventQueue)的概念,直接在main线程中操作UI组件。这可能导致界面卡顿甚至死锁。正确的做法是始终通过SwingUtilities.invokeLater来更新界面,就像我们在main方法中做的那样。
