赞
踩
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 15 |
• Estimate | • 估计这个任务需要多少时间 | 20 | 15 |
Development | 开发 | 600 | 840 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 150 |
• Design Spec | • 生成设计文档 | 30 | 20 |
• Design Review | • 设计复审 | 10 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
• Design | • 具体设计 | 30 | 60 |
• Coding | • 具体编码 | 300 | 480 |
• Code Review | • 代码复审 | 30 | 50 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 50 |
Reporting | 报告 | 100 | 75 |
• Test Repor | • 测试报告 | 30 | 30 |
• Size Measurement | • 计算工作量 | 20 | 15 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 50 | 30 |
合计 | 720 | 930 |
首先设计计算器的基本界面,通过两个StringBuilder类s1、s2来作为数字的载体,字符op作为操作符
通过按钮的监听事件输入数字以及运算符
在对字符串处理后形成的数字通过读取的操作符进行计算
①使用JFrame创建出计算器初始界面,并设置大小等参数
//设置界面的宽高 this.setSize(500, 600); //设置界面的标题 this.setTitle("计算器"); //设置界面置顶 this.setAlwaysOnTop(true); //设置界面居中 this.setLocationRelativeTo(null); //固定界面大小 this.setResizable(false); //设置关闭模式 this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //取消默认放置模式 this.setLayout(null); //给整个界面添加键盘监听事件 this.addKeyListener(this);
②使用JMenuBar为计算器添加菜单,并设置大小等参数
//创建整个菜单对象 JMenuBar jMenuBar = new JMenuBar(); //创建选项下的条目对象 JMenuItem closeItem = new JMenuItem("关闭计算器"); JMenuItem accountItem = new JMenuItem("说明"); //创建菜单上两个选项的对象 JMenu functionJMenu = new JMenu("功能"); JMenu helpJMenu = new JMenu("帮助"); //将每一个选项下的条目添加到选项中 functionJMenu.add(closeItem); helpJMenu.add(accountItem); //给条目绑定事件 closeItem.addActionListener(this); accountItem.addActionListener(this); //将菜单的两个选项添加到菜单中 jMenuBar.add(functionJMenu); jMenuBar.add(helpJMenu); //给整个界面设置菜单 this.setJMenuBar(jMenuBar);
③JTextArea创建显示屏,JButton作为计算器的按钮,创建一个字符串数组与其绑定,并通过addActionListener()绑定事件
private String[] buttons = { "AC", "删除", "%", "/", "7", "8", "9", "*", "4", "5", "6", "-", "1", "2", "3", "+", "00", "0", ".", "=" }; private JButton[] button = new JButton[buttons.length]; JTextArea display = new JTextArea(); //计算器屏幕大小 display.setPreferredSize(new Dimension(450, 200)); //屏幕字体 display.setWrapStyleWord(true display.setRows(2); display.setFont(new Font("Times New Roman", Font.PLAIN, 50)); display.setEditable(false); for (int i = 0; i < button.length; i++) { button[i] = new JButton(" " + buttons[i] + " "); button[i].setPreferredSize(new Dimension(50, 60)); button[i].addActionListener(this); }
④通过JPanel创建面板,将按钮和显示屏添加入面板,再将面板添加至主界面
JPanel p1 = new JPanel();
JPanel p2 = new JPanel();
//放在中心位置
this.add(p1, BorderLayout.CENTER)
//放在下面
this.add(p2, BorderLayout.SOUTH);
p1.setSize(400, 100);
p1.add(display);
//设置按键的布局
p2.setLayout(new GridLayout(5, 4, 5, 5));
for (int i = 0; i < button.length; i++) {
p2.add(button[i]);
}
⑤使用StringBuilder作为数据的载体,通过append()方法存储读取的字符,使用一个默认值为‘$’字符作为操作符,当读取到运算符号时将其覆盖,运算结束时再置为默认值
//定义
StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder();
char op = '$';
⑥处理各个按钮绑定的事件,包括通过按钮读取数据以及菜单
@Override public void actionPerformed(ActionEvent e) { Object obj = e.getSource(); if (obj == closeItem) { //直接关闭虚拟机 System.exit(0); } else if (obj == accountItem) { //创建一个弹窗对象 JDialog jDialog = new JDialog(); //创建一个管理图片的容器对象 JLabel jLabel = new JLabel(new ImageIcon("img\\help.jpg")); jLabel.setBounds(0, 0, 400, 400); jDialog.getContentPane().add(jLabel); jDialog.setSize(781, 780); jDialog.setAlwaysOnTop(true); //弹框居中 jDialog.setLocationRelativeTo(null); //弹框不关闭无法操作下面的界面 jDialog.setModal(true); //让弹框显示出来 jDialog.setVisible(true); } else { if (obj == button[0]) { // AC display.setText(""); s1 = new StringBuilder(); s2 = new StringBuilder(); op = '$'; } else if (obj == button[button.length - 1]) { // = CalAndShow(); } else if (obj == button[1]) { // 删除 if (s1.length() == 0) { //创建一个弹窗对象 JDialog jDialog = new JDialog(); //创建一个容器对象 JLabel jLabel = new JLabel("没有可删除的数字!"); jDialog.getContentPane().add(jLabel); jDialog.setSize(150, 150); jDialog.setAlwaysOnTop(true); //弹框居中 jDialog.setLocationRelativeTo(null); //弹框不关闭无法操作下面的界面 jDialog.setModal(true); //让弹框显示出来 jDialog.setVisible(true); } else if (op == '$') { //删除整个三角函数符号 if (s1.charAt(s1.length() - 1) == 's' || s1.charAt(s1.length() - 1) == 'n') { s1.deleteCharAt(s1.length() - 1); s1.deleteCharAt(s1.length() - 1); } s1.deleteCharAt(s1.length() - 1); display.setText(s1.toString()); } else if (s2.length() == 0) { op = '$'; display.setText(s1.toString()); } else { if (s2.charAt(s2.length() - 1) == 's' || s2.charAt(s2.length() - 1) == 'n') { s2.deleteCharAt(s2.length() - 1); s2.deleteCharAt(s2.length() - 1); } s2.deleteCharAt(s2.length() - 1); display.setText(s1.toString() + op + s2); } } else { for (int i = 2; i < button.length - 1; i++) { if (obj == button[i]) { display.append(buttons[i]); if ((buttons[i].charAt(0) >= '0') && (buttons[i].charAt(0) <= '9') || buttons[i].charAt(0) == '.' || buttons[i].charAt(0) == '√' || buttons[i].charAt(0) == 'Π' || buttons[i].charAt(0) == 's' || buttons[i].charAt(0) == 'c' || buttons[i].charAt(0) == 't' || buttons[i].charAt(0) == '-' && display.getText().endsWith("n" + buttons[i]) || buttons[i].charAt(0) == '-' && display.getText().endsWith("s" + buttons[i])) { if (op == '$') { s1.append(buttons[i]); } else { s2.append(buttons[i]); } } else { if (op != '$') { if (s2.length() == 0) { display.setText(s1.toString() + buttons[i]); } else { String temp = String.valueOf(op); CalAndShow(); display.append(temp); } } op = buttons[i].charAt(0); } break; } } } } }
⑦读取数字完成进行计算时,先对StringBuilder进行处理,返回double类型的数据,若数据异常返回Double.MAX_VALUE;
(单次运算中sin、cos、tan、根号同时出现其二或一种出现多次都视为数据输入异常)
//处理数据 private double dealNum(String s) { if (countTimes(s, ".") > 1) { return Double.MAX_VALUE; } //每次运算不允许多个Π的输入 if (countTimes(s, "Π") > 1) { return Double.MAX_VALUE; } double sign = s.contains("Π") ? Math.PI : 1; int flag = 0; String[] opp = {"√", "sin", "cos", "tan"}; for (String value : opp) { if (s.contains(value)) { flag += countTimes(s, value); } } if (flag == 0) { if (sign == 1) { return Double.parseDouble(s); } else { String temp1 = s.substring(0, s.length() - 1); if (temp1.equals("-")) { return -Math.PI; } else if (temp1.length() == 0) { return Math.PI; } else { return Double.parseDouble(temp1) * sign; } } } else if (flag == 1) { String temp = s.substring(s.charAt(0) == '√' ? 1 : 3); if (sign == Math.PI) { temp = temp.substring(0, temp.length() - 1); } if (temp.equals("-")) { temp = "-1"; } else if (temp.length() == 0 && sign != 1) { temp = "-1"; } if (temp.length() == 0) { return Double.MAX_VALUE; } if (s.charAt(0) == '√') { return Math.sqrt(Double.parseDouble(temp) * sign); } else if (s.charAt(0) == 's') { return Math.sin(Double.parseDouble(temp) * sign); } else if (s.charAt(0) == 'c') { return Math.cos(Double.parseDouble(temp) * sign); } else if (s.charAt(0) == 't') { return Math.tan(Double.parseDouble(temp) * sign); } } return Double.MAX_VALUE; } //子字符串出现次数 public static int countTimes(String s, String s1) { int old = s.length(); String ss = s.replace(s1, ""); return (old - ss.length()) / s1.length(); }
⑧根据操作符op以及返回的数进行计算与显示结果,每次计算后都将结果存入s1,将s2清空
public void CalAndShow() { double num1 = dealNum(s1.toString()); double num2 = 0; if (num1 == Double.MAX_VALUE) { showDataErr(); } if (s2.length() != 0) { num2 = dealNum(s2.toString()); if (num2 == Double.MAX_VALUE) { showDataErr(); } if (op == '+') { num1 += num2; } else if (op == '-') { num1 -= num2; } else if (op == '×') { num1 *= num2; } else if (op == '%') { num1 %= num2; } else if (op == '^') { num1 = Math.pow(num1, num2); } else if (op == '÷') { if (num2 == 0) { showNumErr(); num2 = Double.MAX_VALUE; } else { num1 /= num2; } } s2 = new StringBuilder(); op = '$'; } if (num1 % 1 == 0) { s1 = new StringBuilder(String.valueOf((int) num1)); } else { s1 = new StringBuilder(String.valueOf(num1)); } if (num1 == Double.MAX_VALUE || num2 == Double.MAX_VALUE) { s1 = new StringBuilder(); } display.setText(s1.toString()); } //数据输入错误 private void showDataErr() { //创建一个弹窗对象 JDialog jDialog = new JDialog(); //创建一个容器对象 JLabel jLabel = new JLabel(" 数据输入错误!!!"); jDialog.getContentPane().add(jLabel); jDialog.setSize(150, 150); jDialog.setAlwaysOnTop(true); //弹框居中 jDialog.setLocationRelativeTo(null); //弹框不关闭无法操作下面的界面 jDialog.setModal(true); //让弹框显示出来 jDialog.setVisible(true); } //除数为零报错 private void showNumErr() { //创建一个弹窗对象 JDialog jDialog = new JDialog(); //创建一个容器对象 JLabel jLabel = new JLabel(" 除数不能为零!!!"); jDialog.getContentPane().add(jLabel); jDialog.setSize(150, 150); jDialog.setAlwaysOnTop(true); //弹框居中 jDialog.setLocationRelativeTo(null); //弹框不关闭无法操作下面的界面 jDialog.setModal(true); //让弹框显示出来 jDialog.setVisible(true); }
①在进行删除操作时,只能删除单个字符,需要点击三次删除键才能删除三角函数的符号
改进方法:通过读取屏幕上的内容,若为三角函数符号,则连续删除2个字符串再进行正常的删除操作
//删除整个三角函数符号
if (s1.charAt(s1.length() - 1) == 's' || s1.charAt(s1.length() - 1) == 'n') {
s1.deleteCharAt(s1.length() - 1);
s1.deleteCharAt(s1.length() - 1);
}
②在处理数据过程中,若遇到Π,定义一个标识sign,默认值为1,若字符串中出现Π,设sign为Π,只需最后返回的数字都 * sign进而减少代码长度
double sign = s.contains("Π") ? Math.PI : 1;
......
return Double.parseDouble(temp1) * sign;
利用Junit框架进行单元测试
通过随机数得到两个数据,对各种运算进行100次测试
如下的加法测试与sin函数测试
// 加法测试 @Test public void AddTest() { Random r = new Random(); double[] ans = new double[100]; double[] res = new double[100]; for (int i = 0; i < 100; i++) { double x = r.nextDouble(); double y = r.nextDouble(); ans[i] = x + y; cul.s1 = new StringBuilder(String.valueOf(x)); cul.s2 = new StringBuilder(String.valueOf(y)); cul.op = '+'; cul.CalAndShow(); res[i] = Double.parseDouble(cul.display.getText()); } Assert.assertArrayEquals("Error", ans, res, 0.01); } //sin函数 @Test public void sinTest() { Random r = new Random(); double[] ans = new double[100]; double[] res = new double[100]; for (int i = 0; i < 100; i++) { double x = r.nextDouble(); ans[i] = Math.sin(x); cul.s1 = new StringBuilder("sin" + x); cul.CalAndShow(); res[i] = Double.parseDouble(cul.display.getText()); } Assert.assertArrayEquals("Error", ans, res, 0.01); }
测试结果如下:
输入异常数据或除数为零时
通过弹窗进行报错
心路历程:从一开始设计了只有加减乘除取余操作的简易计算器(带后缀_Pre的文件),后来根据作业要求在原来计算器的基础上一步步添加了科学计算(次方、三角函数等)
学习收获:通过本次作业,我学会了如何使用Java创建具有可视化界面以及软件单元测试,提高了Java的编程能力,在编码过程中的一次次调试也增强了发现与解决问题的能力
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。