Java Swing 个人所得税计算工具
这是一个基于 Java Swing 开发的个人所得税计算模拟器,模拟了官方个税 APP 的核心功能。项目包含完整的图形界面和核心计算逻辑,适合 Java 初学者理解 Swing 框架应用及税务计算业务逻辑。
核心功能
- 综合所得年度汇算:支持输入年收入、专项扣除等信息进行计算
- 税率表查询:内置 2023 年最新个税税率表
- 专项扣除设置:提供养老、医疗、住房等常见扣除项参考
- 结果展示:实时显示应纳税额、税后收入及月度估算
核心算法与精度处理
在涉及金额计算时,浮点数精度问题必须重视。这里我们使用 BigDecimal 来确保财务数据的准确性,避免 double 类型可能带来的误差。
package com.tax.calculator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
/**
* 个人所得税计算器
* 实现 2023 年最新个税计算规则
*/
public class TaxCalculator {
// 税率表(年度综合所得)
private static final TaxBracket[] TAX_BRACKETS = {
new TaxBracket(0, 36000, 0.03, 0),
new TaxBracket(36000, 144000, 0.10, 2520),
new TaxBracket(144000, 300000, 0.20, 16920),
new TaxBracket(300000, 420000, 0.25, 31920),
new TaxBracket(420000, 660000, 0.30, 52920),
new TaxBracket(660000, 960000, 0.35, 85920),
new TaxBracket(960000, Double.MAX_VALUE, 0.45, 181920)
};
// 月度专项扣除标准
private static final Map<String, BigDecimal> SPECIAL_DEDUCTIONS = new HashMap<>();
static {
// 初始化专项扣除标准(月度)
SPECIAL_DEDUCTIONS.put("养老", new BigDecimal("1000"));
SPECIAL_DEDUCTIONS.put("医疗", new BigDecimal("200"));
SPECIAL_DEDUCTIONS.put("失业", new BigDecimal("50"));
SPECIAL_DEDUCTIONS.put("住房", new BigDecimal("1200"));
SPECIAL_DEDUCTIONS.put("子女教育", new BigDecimal("1000"));
SPECIAL_DEDUCTIONS.put("继续教育", new BigDecimal("400"));
SPECIAL_DEDUCTIONS.put("大病医疗", new BigDecimal("8000"));
SPECIAL_DEDUCTIONS.put("房贷利息", new BigDecimal("1000"));
SPECIAL_DEDUCTIONS.put("住房租金", new BigDecimal("1500"));
SPECIAL_DEDUCTIONS.put("赡养老人", new BigDecimal("2000"));
}
/**
* 计算年度个人所得税
*/
public static TaxResult calculateAnnualTax(
double annualIncome,
double specialDeductions,
double additionalDeductions) {
// 基本扣除费用(年度)
final double BASIC_DEDUCTION = 60000;
// 转换为 BigDecimal 确保精度
BigDecimal income = BigDecimal.valueOf(annualIncome);
BigDecimal special = BigDecimal.valueOf(specialDeductions);
BigDecimal additional = BigDecimal.valueOf(additionalDeductions);
BigDecimal basic = BigDecimal.valueOf(BASIC_DEDUCTION);
// 计算应纳税所得额
BigDecimal taxableIncome = income
.subtract(special)
.subtract(additional)
.subtract(basic)
.max(BigDecimal.ZERO); // 不能为负数
// 查找适用税率
TaxBracket bracket = findTaxBracket(taxableIncome.doubleValue());
// 计算应纳税额
BigDecimal taxAmount = taxableIncome
.multiply(BigDecimal.valueOf(bracket.getRate()))
.subtract(BigDecimal.valueOf(bracket.getQuickDeduction()));
// 确保税额不为负数
taxAmount = taxAmount.max(BigDecimal.ZERO);
// 计算税后收入
BigDecimal afterTaxIncome = income.subtract(taxAmount);
return new TaxResult(
income.doubleValue(),
taxableIncome.doubleValue(),
taxAmount.doubleValue(),
afterTaxIncome.doubleValue(),
bracket
);
}
/**
* 根据应纳税所得额查找适用税率
*/
private static TaxBracket findTaxBracket(double taxableIncome) {
for (TaxBracket bracket : TAX_BRACKETS) {
if (taxableIncome >= bracket.getLowerBound() &&
taxableIncome < bracket.getUpperBound()) {
return bracket;
}
}
return TAX_BRACKETS[TAX_BRACKETS.length - 1];
}
/**
* 获取专项扣除标准
*/
public static Map<String, BigDecimal> getSpecialDeductionStandards() {
return new HashMap<>(SPECIAL_DEDUCTIONS);
}
/**
* 税率档次类
*/
public static class TaxBracket {
private final double lowerBound;
private final double upperBound;
private final double rate;
private final double quickDeduction;
public TaxBracket(double lowerBound, double upperBound,
double rate, double quickDeduction) {
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.rate = rate;
this.quickDeduction = quickDeduction;
}
// Getter 方法
public double getLowerBound() { return lowerBound; }
public double getUpperBound() { return upperBound; }
public double getRate() { return rate; }
public double getQuickDeduction() { return quickDeduction; }
@Override
public String toString() {
return String.format("税率:%.1f%%, 速算扣除数:%.0f", rate * 100, quickDeduction);
}
}
/**
* 计算结果类
*/
public static class TaxResult {
private final double totalIncome; // 总收入
private final double taxableIncome; // 应纳税所得额
private final double taxAmount; // 应纳税额
private final double afterTaxIncome; // 税后收入
private final TaxBracket taxBracket; // 适用税率档次
public TaxResult(double totalIncome, double taxableIncome,
double taxAmount, double afterTaxIncome,
TaxBracket taxBracket) {
this.totalIncome = totalIncome;
this.taxableIncome = taxableIncome;
this.taxAmount = taxAmount;
this.afterTaxIncome = afterTaxIncome;
this.taxBracket = taxBracket;
}
// Getter 方法
public double getTotalIncome() { return totalIncome; }
public double getTaxableIncome() { return taxableIncome; }
public double getTaxAmount() { return taxAmount; }
public double getAfterTaxIncome() { return afterTaxIncome; }
public TaxBracket getTaxBracket() { return taxBracket; }
@Override
public String toString() {
return String.format(
"计算结果:\n" +
"总收入:¥%.2f\n" +
"应纳税所得额:¥%.2f\n" +
"应纳税额:¥%.2f\n" +
"税后收入:¥%.2f\n" +
"适用税率:%s",
totalIncome, taxableIncome, taxAmount, afterTaxIncome, taxBracket
);
}
}
}
GUI 界面实现
Swing 开发中,线程安全是个关键点。主窗口创建必须在 EDT(事件分发线程)上执行,所以我们使用了 SwingUtilities.invokeLater。
package com.tax.calculator;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.util.Map;
/**
* 个税计算器主界面
*/
public class TaxCalculatorFrame extends JFrame {
private JTextField incomeField;
private JTextField specialDeductionField;
private JTextField additionalDeductionField;
private JTextArea resultArea;
private JComboBox<String> deductionTypeCombo;
private JLabel standardLabel;
public TaxCalculatorFrame() {
initUI();
initData();
}
private void initUI() {
setTitle("个人所得税计算模拟器 v1.0");
setSize(800, 600);
setLocationRelativeTo(null);
// 主面板
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// 输入面板
mainPanel.add(createInputPanel(), BorderLayout.NORTH);
// 结果面板
mainPanel.add(createResultPanel(), BorderLayout.CENTER);
// 按钮面板
mainPanel.add(createButtonPanel(), BorderLayout.SOUTH);
add(mainPanel);
}
private JPanel createInputPanel() {
JPanel panel = new JPanel(new GridLayout(5, 2, 10, 10));
panel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(Color.GRAY),
"个税计算参数输入",
TitledBorder.LEFT,
TitledBorder.TOP
));
// 年度总收入
panel.add(new JLabel("年度总收入 (¥):"));
incomeField = new JTextField("120000");
panel.add(incomeField);
// 专项扣除
panel.add(new JLabel("三险一金专项扣除 (¥):"));
specialDeductionField = new JTextField("10000");
panel.add(specialDeductionField);
// 专项附加扣除
panel.add(new JLabel("专项附加扣除 (¥):"));
additionalDeductionField = new JTextField("3000");
panel.add(additionalDeductionField);
// 扣除标准查询
panel.add(new JLabel("扣除项目查询:"));
JPanel queryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
deductionTypeCombo = new JComboBox<>();
queryPanel.add(deductionTypeCombo);
standardLabel = new JLabel("标准:¥0");
queryPanel.add(standardLabel);
panel.add(queryPanel);
return panel;
}
private JScrollPane createResultPanel() {
resultArea = new JTextArea();
resultArea.setEditable(false);
resultArea.setFont(new Font("宋体", Font.PLAIN, 14));
resultArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JScrollPane scrollPane = new JScrollPane(resultArea);
scrollPane.setBorder(BorderFactory.createTitledBorder("计算结果"));
return scrollPane;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
// 计算按钮
JButton calculateBtn = new JButton("计算个税");
calculateBtn.setFont(new Font("微软雅黑", Font.BOLD, 14));
calculateBtn.setBackground(new Color(70, 130, 180));
calculateBtn.setForeground(Color.WHITE);
calculateBtn.setPreferredSize(new Dimension(120, 40));
calculateBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
calculateTax();
}
});
// 清除按钮
JButton clearBtn = new JButton("清除结果");
clearBtn.setFont(new Font("微软雅黑", Font.PLAIN, 14));
clearBtn.setPreferredSize(new Dimension(120, 40));
clearBtn.addActionListener(e -> clearResults());
// 税率表按钮
JButton rateTableBtn = new JButton("查看税率表");
rateTableBtn.setFont(new Font("微软雅黑", Font.PLAIN, 14));
rateTableBtn.setPreferredSize(new Dimension(120, 40));
rateTableBtn.addActionListener(e -> showTaxRateTable());
panel.add(calculateBtn);
panel.add(clearBtn);
panel.add(rateTableBtn);
return panel;
}
private void initData() {
// 初始化扣除类型下拉框
Map<String, BigDecimal> standards = TaxCalculator.getSpecialDeductionStandards();
for (String key : standards.keySet()) {
deductionTypeCombo.addItem(key);
}
// 添加下拉框监听
deductionTypeCombo.addActionListener(e -> {
String selected = (String) deductionTypeCombo.getSelectedItem();
if (selected != null) {
BigDecimal standard = standards.get(selected);
standardLabel.setText(String.format("标准:¥%.2f", standard));
}
});
// 默认选择第一个
if (deductionTypeCombo.getItemCount() > 0) {
deductionTypeCombo.setSelectedIndex(0);
}
}
/**
* 执行个税计算
*/
private void calculateTax() {
try {
// 获取输入值
double income = Double.parseDouble(incomeField.getText());
double special = Double.parseDouble(specialDeductionField.getText());
double additional = Double.parseDouble(additionalDeductionField.getText());
// 验证输入
if (income < 0 || special < 0 || additional < 0) {
JOptionPane.showMessageDialog(this,
"输入值不能为负数!",
"输入错误",
JOptionPane.WARNING_MESSAGE);
return;
}
// 执行计算
TaxCalculator.TaxResult result = TaxCalculator.calculateAnnualTax(
income, special, additional);
// 显示结果
displayResult(result);
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(this,
"请输入有效的数字!",
"输入错误",
JOptionPane.ERROR_MESSAGE);
}
}
/**
* 显示计算结果
*/
private void displayResult(TaxCalculator.TaxResult result) {
StringBuilder sb = new StringBuilder();
sb.append("=== 个人所得税计算结果 ===\n\n");
sb.append(String.format("年度总收入:¥%,.2f\n", result.getTotalIncome()));
sb.append(String.format("应纳税所得额:¥%,.2f\n", result.getTaxableIncome()));
sb.append(String.format("适用税率档次:%s\n", result.getTaxBracket()));
sb.append(String.format("应纳个人所得税:¥%,.2f\n", result.getTaxAmount()));
sb.append(String.format("年度税后收入:¥%,.2f\n\n", result.getAfterTaxIncome()));
// 计算月度数据
double monthlyTax = result.getTaxAmount() / 12;
double monthlyAfterTax = result.getAfterTaxIncome() / 12;
sb.append("=== 月度数据估算 ===\n");
sb.append(String.format("月度平均纳税:¥%,.2f\n", monthlyTax));
sb.append(String.format("月度税后收入:¥%,.2f\n\n", monthlyAfterTax));
// 添加税务建议
sb.append("=== 税务优化建议 ===\n");
if (result.getTaxableIncome() > 0) {
sb.append("1. 充分利用专项附加扣除项目\n");
sb.append("2. 合理规划年度收入时间分布\n");
sb.append("3. 符合条件的捐赠可税前扣除\n");
} else {
sb.append("当前收入无需缴纳个人所得税\n");
}
resultArea.setText(sb.toString());
resultArea.setCaretPosition(0);
}
/**
* 显示税率表
*/
private void showTaxRateTable() {
StringBuilder sb = new StringBuilder();
sb.append("=== 个人所得税税率表(综合所得) ===\n\n");
sb.append("级数 | 全年应纳税所得额 | 税率 | 速算扣除数\n");
sb.append("----|-----------------|------|----------\n");
TaxCalculator.TaxBracket[] brackets = new TaxCalculator.TaxBracket[]{
new TaxCalculator.TaxBracket(0, 36000, 0.03, 0),
new TaxCalculator.TaxBracket(36000, 144000, 0.10, 2520),
new TaxCalculator.TaxBracket(144000, 300000, 0.20, 16920),
new TaxCalculator.TaxBracket(300000, 420000, 0.25, 31920),
new TaxCalculator.TaxBracket(420000, 660000, 0.30, 52920),
new TaxCalculator.TaxBracket(660000, 960000, 0.35, 85920),
new TaxCalculator.TaxBracket(960000, Double.MAX_VALUE, 0.45, 181920)
};
for (int i = 0; i < brackets.length; i++) {
TaxCalculator.TaxBracket b = brackets[i];
String range;
if (b.getUpperBound() == Double.MAX_VALUE) {
range = String.format("超过%,.0f 元", b.getLowerBound());
} else {
range = String.format("%,.0f - %, .0f 元",
b.getLowerBound(), b.getUpperBound());
}
sb.append(String.format("%-4d| %-15s | %.1f%% | %, .0f 元\n",
i + 1, range, b.getRate() * 100, b.getQuickDeduction()));
}
sb.append("\n注:应纳税所得额 = 年度收入 - 6 万元 (基本扣除) - 专项扣除 - 专项附加扣除\n");
resultArea.setText(sb.toString());
}
/**
* 清除结果
*/
private void clearResults() {
resultArea.setText("");
}
}
编译与运行
确保环境已安装 Java 8 或更高版本,并支持 Swing。
# 编译所有 Java 文件
javac -d . com/tax/calculator/*.java
# 运行程序
java com.tax.calculator.PersonalIncomeTaxApp
扩展建议
如果打算在此基础上继续完善,可以考虑以下方向:
- 数据持久化:添加本地文件保存/加载历史计算记录的功能。
- 图表展示:引入 JFreeChart 库,将收入与税负比例可视化。
- 联网更新:增加接口自动同步最新的个税政策与税率调整。
- 导出功能:支持将计算结果导出为 PDF 或 Excel 格式。
注意事项
本程序仅作为技术学习与演示用途,实际报税请以官方税务系统为准。税率政策如有更新,需相应修改代码中的税率表数据。计算结果仅供参考,不构成专业税务建议。


