JavaSE练习,JDBC驱动,基于swing库的带登录功能计算器

发布时间 2023-11-27 21:52:02作者: aquarrr

一、前言

  本次作业是基于上次的计算器功能所做的改进,通过JDBC连接MySQL增加了登录与注册功能,并对计算器所作的运算进行了记录。虽然基于上次的作业所作,但是设计编写的模块大部分与之无关(登录注册自然与计算器无关)。所以本次作业属于再开发而不是运营维护。

二、概要设计

  首先是对登录与注册界面的设计,登录界面需要登录和注册两个按钮,登录按钮是登录功能,注册按钮是打开注册页面。且注册界面的关闭不会引起登录界面的关闭,而登录界面的关闭会引起注册界面的关闭。

  总得功能设计如下:

 

  要处理登录数据,我们需要将登录数据存入数据库中,用户类的er图如下:

  首先是对数据库的设计,设计用户表,包含了Uid,用户名,密码,手机号码,我们设uid为主键并令其自增,但我们也要设置用户名唯一性约束,这样方便了用户的登录,可以让用户通过用户名登录并确保不会登录到别人的账号。为了软件的安全性考虑,用户名和密码都是非空的。用户表如下:

CREATE TABLE userinfo(
uid INT,CONSTRAINT PK_Uid PRIMARY KEY(uid),
uname VARCHAR(20) NOT NULL,CONSTRAINT UQ_Uname UNIQUE(uname),
pass VARCHAR(20) NOT NULL,
uphone VARCHAR(15)
)

ALTER TABLE userinfo MODIFY uid INT AUTO_INCREMENT

有了用户表,我们还要设计一个记录表,用于记录用户的计算记录,每个用户的计算机记录要分开,所以记录表包含三列,外键用户名,计算公式,答案,记录表如下:

CREATE TABLE record(
uname VARCHAR(20), CONSTRAINT FK_uname FOREIGN KEY record(uname) REFERENCES userinfo(uname),
caled NVARCHAR(32),
ans NVARCHAR(10)
)

测试所得的数据库表情况如下,

  

 三、代码设计

  这次的新功能都是基于数据库的,为了实现新功能,首先我们要提供数据库操作的大接口,该类简单地给出了sql语句执行的接口,代码如下:

 1 package DAO;
 2 
 3 import java.sql.*;
 4 public class connectMysql {
 5 
 6     private static Connection connection;
 7     private static Statement statement;
 8     private ResultSet resultSet;
 9     private ResultSetMetaData metaData;
10 
11     // 导入包时执行,静态初始化块
12     static {
13         try {
14             Class.forName("com.mysql.jdbc.Driver");
15             String url = "jdbc:mysql://127.0.0.1:3306/calculator?characterEncoding=UTF-8";
16             String user = "root";
17             String password = "abcd1234";
18             connection = DriverManager.getConnection(url, user,password);
19             statement = connection.createStatement();
20         }catch (Exception e) {
21             e.printStackTrace();
22         }
23     }
24 
25     public int executeSCL(String sql) {
26         int temp = 0;
27         try {
28             temp = statement.executeUpdate(sql);
29         }catch (Exception exception) {
30             exception.printStackTrace();
31         }
32         return temp;
33     }
34 
35     public ResultSet executeSQL(String sql) {
36         try {
37             this.resultSet = statement.executeQuery(sql);
38             this.metaData = this.resultSet.getMetaData();
39         }catch (Exception exception) {
40             exception.printStackTrace();
41         }
42         return this.resultSet;
43     }
44 
45 
46     public ResultSet getResultSet() {
47         return resultSet;
48     }
49 
50     public static void main(String[] args) throws Exception {
51         int i = statement.executeUpdate("INSERT INTO userinfo(uname, pass) VALUES ('aqua','123456')");
52     }
53 }

藉由此接口,我们可以将数据库中的信息抽象化为两个类,并且将查询或插入静态方法封装到类中,代码如下,分别为userinfo类和record类:

  1 package VO;
  2 
  3 import DAO.connectMysql;
  4 
  5 import java.sql.ResultSet;
  6 import java.util.Iterator;
  7 import java.util.LinkedList;
  8 
  9 /******************* virtual DataBase *******************
 10 *  ---------------------------------------------------  *
 11 *  |  uid  |    uname   |  password  |     phone     |  *
 12 *  | 00001 |   aquarium |  ********  |  130****3721  |  *
 13 *  | 00002 |   aquarrrr |  ********  |  188****4083  |  *
 14 *  ---------------------------------------------------  *
 15 ********************************************************/
 16 
 17 
 18 
 19 public class userInfo {
 20     private int uid;
 21     private String uname;
 22     private String password;
 23     private String phone;
 24 
 25 
 26     public int getUid() {
 27         return uid;
 28     }
 29 
 30     public String getPassword() {
 31         return password;
 32     }
 33 
 34     public String getPhone() {
 35         return phone;
 36     }
 37 
 38     public String getUname() {
 39         return uname;
 40     }
 41 
 42     public void setPassword(String password) {
 43         this.password = password;
 44     }
 45 
 46     public void setPhone(String phone) {
 47         this.phone = phone;
 48     }
 49 
 50     public void setUid(int uid) {
 51         this.uid = uid;
 52     }
 53 
 54     public void setUname(String uname) {
 55         this.uname = uname;
 56     }
 57 
 58     public userInfo(int uid, String uname, String password, String phone) {
 59         this.uid = uid;
 60         this.uname = uname;
 61         this.password = password;
 62         this.phone = phone;
 63     }
 64 
 65     public userInfo(String uname, String password, String phone) {
 66         this.uname = uname;
 67         this.password = password;
 68         this.phone = phone;
 69     }
 70 
 71     public userInfo() {
 72         super();
 73     }
 74 
 75     // 查询数据库中的用户并返回结果表(往往只有一个)
 76     public static LinkedList<userInfo> queryUser(String name) {
 77         LinkedList<userInfo> userlist = new LinkedList<userInfo>();
 78         userInfo temp = null;
 79         try {
 80             connectMysql connectmysql = new connectMysql();
 81             ResultSet resultSet = connectmysql.executeSQL("SELECT * FROM userinfo WHERE uname='" + name + "'");
 82             while (resultSet.next()) {
 83                 int uid = resultSet.getInt("uid");
 84                 String uname = resultSet.getString("uname");
 85                 String pass = resultSet.getString("pass");
 86                 String uphone = resultSet.getString("uphone");
 87                 temp = new userInfo(uid, uname, pass, uphone);
 88                 userlist.add(temp);
 89             }
 90         }catch (Exception e) {
 91             e.printStackTrace();
 92         }
 93         return userlist;
 94     }
 95 
 96     // 添加用户
 97     public static int insertUser(userInfo user) {
 98         int updated = 0;
 99         try {
100             connectMysql connectmysql = new connectMysql();
101             updated = connectmysql.executeSCL("INSERT INTO userinfo(uname,pass,uphone) VALUES ('"+ user.getUname() +"','"+user.getPassword()+"','"+user.getPhone()+"')");
102         }catch (Exception e) {
103             e.printStackTrace();
104         }
105         return updated;
106     }
107 
108 
109     @Override
110     public String toString() {
111         return this.getUname() + " : " + this.getPassword();
112     }
113 
114     @Override
115     public int hashCode() {
116         return this.getUid();
117     }
118 
119     public static void main(String[] args) {
120         LinkedList l = queryUser("aquarium");
121         System.out.println(l.size());
122         Iterator iterator = l.iterator();
123         while (iterator.hasNext()) {
124             System.out.println(iterator.next());
125         }
126     }
127 }
 1 package VO;
 2 
 3 import DAO.connectMysql;
 4 
 5 public class record {
 6 /******** virtual DataBase ********
 7  * ------------------------------ *
 8  * |  uname   |  Ucaled  | uans | *
 9  * | aquarium |  11+1x2  |  13  | *
10  * | aquarrrr |  2x5+22  |  32  | *
11  * ------------------------------ *
12  **********************************/
13 
14     private String uname;
15     private String caled;
16     private String ans;
17 
18     public void setUname(String uname) {
19         this.uname = uname;
20     }
21 
22     public void setAns(String ans) {
23         this.ans = ans;
24     }
25 
26     public void setCaled(String caled) {
27         this.caled = caled;
28     }
29 
30     public String getCaled() {
31         return caled;
32     }
33 
34     public String getUname() {
35         return uname;
36     }
37 
38     public String getAns() {
39         return ans;
40     }
41 
42     public record(String uname, String caled, String ans) {
43         this.uname = uname;
44         this.caled = caled;
45         this.ans = ans;
46     }
47 
48     @Override
49     public String toString() {
50         return this.caled+"="+this.ans;
51     }
52 
53     public static int insertCal(record re) {
54         int updated = 0;
55         try {
56             connectMysql connectmysql = new connectMysql();
57             updated = connectmysql.executeSCL("INSERT INTO record(uname,caled,ans) VALUES ('"+ re.getUname() +"','"+re.getCaled()+"','"+re.getAns()+"')");
58         }catch (Exception e) {
59             e.printStackTrace();
60         }
61         return updated;
62     }
63 
64 }

软件的设计由后端推动,我们在有了这些接口后,就可以设计前段的界面,这次尝试一下监听与框架剥离,框架代码如下:

  1 package com.aquarrr.calogin;
  2 
  3 // get EXAMPLE :: calculator.EXAMPLE
  4 import Service.calculator;
  5 
  6 import javax.swing.*;
  7 import java.awt.*;
  8 
  9 public class calogin extends JFrame {
 10 
 11     Font song = new Font("宋体",Font.BOLD,(int) (EXAMPLE * 0.2));
 12 
 13     protected static int EXAMPLE = calculator.EXAMPLE;
 14 
 15 
 16     /***************************************** 加拿大 ************************************************/
 17 
 18 
 19     // 三种不同状态,0:默认 1:没有相匹配用户名 2:密码错误
 20     public static final int DEFAULT = 0;
 21     public static final int USER_NOT_FOUND = 1;
 22     public static final int WRONG_PASSWORD = 2;
 23 
 24     public static String[] messages = {"请输入用户名及密码", "用户不存在", "用户名密码不匹配"};
 25 
 26     private JLabel message = new JLabel();
 27 
 28     public JLabel getMessage() {
 29         return this.message;
 30     }
 31 
 32     // 三种状态
 33     protected static void setmessage(JLabel state, int statement) {
 34         state.setText(messages[statement]);
 35         state.setForeground(Color.red);
 36     }
 37 
 38     // 添加初始化状态栏
 39     protected void addstatement() {
 40         // 已自定义设置
 41         setmessage(this.message, calogin.DEFAULT);
 42         this.message.setFont(this.song);
 43         this.message.setForeground(Color.black);
 44         // 居中并设置大小
 45         this.message.setPreferredSize(new Dimension(4 * EXAMPLE, (int) (0.5  * EXAMPLE)));
 46         this.message.setHorizontalAlignment(SwingConstants.CENTER);
 47         // 直接添加至北方
 48         this.add(this.message,BorderLayout.NORTH);
 49     }
 50 
 51 
 52     /***************************************** 中央情报局 **************************************************/
 53 
 54     // central intelligence agency,说明该部分负责接收用户输入信息
 55     private JPanel CIA = new JPanel();
 56     private JLabel uname = new JLabel("用户名:");
 57     private JTextField unameInput = new JTextField();
 58     private JLabel upass = new JLabel("密码:");
 59     private JPasswordField upassInput = new JPasswordField();
 60 
 61     public String getUname() {
 62         return this.unameInput.getText();
 63     }
 64 
 65     public String getUpass() {
 66         return this.upassInput.getText();
 67     }
 68 
 69     // 设置提示词大小
 70     private Dimension labelSize = new Dimension((int) (0.8 * EXAMPLE), (int) (0.6 * EXAMPLE));
 71 
 72     // 设置输入框大小
 73     private Dimension fieldSize = new Dimension((int) (2.5 * EXAMPLE), (int) (0.6 * EXAMPLE));
 74 
 75 
 76     // 添加用户名密码及其输入框
 77     protected void addCIA() {
 78         // 设置字体
 79         this.upassInput.setFont(this.song);
 80         this.unameInput.setFont(this.song);
 81         this.uname.setFont(this.song);
 82         this.upass.setFont(this.song);
 83         // 设置大小
 84         this.uname.setPreferredSize(this.labelSize);
 85         this.upass.setPreferredSize(this.labelSize);
 86         this.unameInput.setPreferredSize(this.fieldSize);
 87         this.upassInput.setPreferredSize(this.fieldSize);
 88         // 修改布局
 89         FlowLayout FL = new FlowLayout();
 90         FL.setVgap((int) (0.15 * EXAMPLE));
 91         this.CIA.setLayout(FL);
 92         // 添加到仪表
 93         this.CIA.add(this.uname);
 94         this.CIA.add(this.unameInput);
 95         this.CIA.add(this.upass);
 96         this.CIA.add(this.upassInput);
 97         // 设置仪表到框架
 98         this.CIA.setBackground(Color.LIGHT_GRAY);
 99         this.add(this.CIA, BorderLayout.CENTER);
100     }
101 
102 
103 
104     /********************************************* 墨西哥 ************************************************/
105 
106     private JPanel bottom = new JPanel();
107     private JButton signIn = new JButton("登录");
108     private JButton signUp = new JButton("注册");
109 
110     protected void addBottom() {
111         // 设置按钮字体及大小
112         this.signIn.setFont(this.song);
113         this.signUp.setFont(this.song);
114         this.signIn.setPreferredSize(new Dimension((int) (1.6 * EXAMPLE), (int) (0.5 * EXAMPLE)));
115         this.signUp.setPreferredSize(new Dimension((int) (1.6 * EXAMPLE), (int) (0.5 * EXAMPLE)));
116         // 修改布局
117         FlowLayout FL = new FlowLayout();
118         FL.setHgap((int) (0.2 * EXAMPLE));
119         this.bottom.setLayout(FL);
120         // 添加到仪表盘添加到框架
121         this.bottom.setPreferredSize(new Dimension((int) (3.5 * EXAMPLE), (int) (0.6 * EXAMPLE)));
122         this.bottom.add(this.signIn);
123         this.bottom.add(this.signUp);
124         this.add(this.bottom, BorderLayout.SOUTH);
125     }
126 
127 
128 
129     /****************************************** 虚假的初始化 *********************************************/
130 
131 
132     public void  __init__() {
133         // 设置框架名称
134         this.setTitle("log in");
135         // 设置框架3 : 4
136         this.setSize((int) 4 * EXAMPLE, (int) 3 * EXAMPLE);
137         this.setResizable(false);
138         this.setLocationRelativeTo(null);
139         // 设计框架可见
140         this.setVisible(true);
141         // 设计关闭功能
142         this.setDefaultCloseOperation(EXIT_ON_CLOSE);
143         // 设计方向布局
144         this.setLayout(new BorderLayout());
145     }
146 
147     /*********************************************添加监听*********************************************/
148 
149     // 设置监听类并传输参数
150     public void setListener() {
151         loginListoner loginListoner = new loginListoner();
152         this.signIn.addActionListener(loginListoner);
153         this.signUp.addActionListener(loginListoner);
154         loginListoner.setLogin(this);
155     }
156 
157     /***************************************** 真正的初始化 *******************************************/
158 
159     public calogin() throws HeadlessException {
160         this.__init__();
161         this.addstatement();
162         this.addCIA();
163         this.addBottom();
164         this.setListener();
165     }
166 
167     public static void main(String[] args) {
168         calogin calogin = new calogin();
169     }
170 }

运行结果如下:

设置监听代码如下:

 1 package com.aquarrr.calogin;
 2 
 3 // 自定义类
 4 import Service.signUp;
 5 import VO.userInfo;
 6 import Service.calculator;
 7 
 8 import java.awt.event.ActionEvent;
 9 import java.awt.event.ActionListener;
10 import java.util.LinkedList;
11 
12 public class loginListoner implements ActionListener {
13 
14 
15     /************************************* 封装连接数据库方法 **************************************/
16 
17 
18     public static String queuePass(String uname) {
19         String SQLpass = "";
20         LinkedList<userInfo> userlist = userInfo.queryUser(uname);
21         int num = userlist.size();
22         if (num == 1){
23             SQLpass = userlist.pop().getPassword();
24         }
25         return SQLpass;
26     }
27 
28 
29 
30     /************************************** 事件监听处理区 ******************************************/
31 
32 
33     // 被传输的组件
34     calogin login;
35 
36     // 传输方法
37     public void setLogin(calogin login) {
38         this.login = login;
39     }
40 
41     // 监听事件
42     @Override
43     public void actionPerformed(ActionEvent e) {
44         String command = e.getActionCommand();
45         if(command.equals("登录")) {
46             String uname = login.getUname();
47             String upass = login.getUpass();
48             String rpass = queuePass(uname);
49             // 检测用户名
50             if(uname.equals("") ||upass.equals("")) {
51                 calogin.setmessage(login.getMessage(), calogin.DEFAULT);
52             } else if (rpass.equals("")) {
53                 calogin.setmessage(login.getMessage(), calogin.USER_NOT_FOUND);
54             } else if (!upass.equals(rpass)) {
55                 calogin.setmessage(login.getMessage(), calogin.WRONG_PASSWORD);
56             } else {
57                 calculator c = new calculator(uname);
58                 login.dispose();
59             }
60         } else if (command.equals("注册")) {
61             signUp signup = new signUp();
62         }
63     }
64 
65     public static void main(String[] args) {
66         System.out.println(queuePass("ftaqua"));
67     }
68 }

可以看到,在程序中,当用户有非法输入或错误输入时,上方的提示字和字的颜色不一样,有以下状态:

     

接下来设计注册功能,代码如下:

  1 package Service;
  2 
  3 // get EXAMPLE :: calculator.EXAMPLE
  4 import Service.calculator;
  5 import VO.userInfo;
  6 
  7 import javax.swing.*;
  8 import java.awt.*;
  9 import java.awt.event.ActionEvent;
 10 import java.awt.event.ActionListener;
 11 
 12 public class signUp extends JFrame implements ActionListener {
 13 
 14     /**************************************** 定义所有组件 ******************************************/
 15 
 16     // 定义所有组件
 17     private JLabel username;
 18     private JLabel password;
 19     private JLabel confirmPassword;
 20     private JLabel phone;
 21     private final JLabel message = new JLabel("test",SwingConstants.CENTER);
 22     private JTextField usernameField, phoneField;
 23     private JPasswordField passwordField, confirmPasswordField;
 24     private JButton signup;
 25 
 26     // 设置静态变量
 27     protected static int EXAMPLE = calculator.EXAMPLE;
 28     private static final Font song = new Font("宋体",Font.BOLD,(int) (EXAMPLE * 0.2));
 29     // 0,默认及不合法输入 1,用户名已存在 2,两个密码不一样
 30     public static String[] messages = {"请输入用户名及密码(二者不宜过长)", "用户已存在", "两次密码不一样"};
 31 
 32     public int DEFAULT = 0;
 33     public int EXIST_USER = 1;
 34     public int UNEQUAL_PASS = 2;
 35     private final Dimension filedSize = new Dimension((int) (EXAMPLE * 2.5),(int) (EXAMPLE * 0.3));
 36 
 37     // 管理message
 38     protected void setmessage(int statement) {
 39         this.message.setText(messages[statement]);
 40         this.message.setForeground(Color.red);
 41     }
 42 
 43     // 构造方法
 44     public signUp() {
 45         // 设置窗口标题
 46         super("signUp");
 47         // 设置窗口大小
 48         this.setSize(4 * EXAMPLE, 3 * EXAMPLE);
 49         // 设置窗口居中
 50         this.setLocationRelativeTo(null);
 51         // 设置窗口关闭时退出程序
 52         this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
 53         // 设置窗口布局为网格布局,4行1列,水平间距10,垂直间距20
 54         this.setLayout(new GridLayout(6, 1, (int) (0.1 * EXAMPLE), (int) (0.2 * EXAMPLE)));
 55         // 初始化组件
 56         this.initComponents();
 57         // 添加组件到窗口
 58         this.addComponents();
 59         // 添加监听事件
 60         this.addListener();
 61         // 设置窗口可见
 62         this.setVisible(true);
 63         this.setResizable(false);
 64     }
 65 
 66     // 初始化组件的方法
 67     private void initComponents() {
 68         // 创建标签
 69         this.username = new JLabel("  用户名:");
 70         this.username.setFont(song);
 71         this.password = new JLabel("   密码:");
 72         this.password.setFont(song);
 73         this.confirmPassword = new JLabel("再次输入:");
 74         this.confirmPassword.setFont(song);
 75         this.phone = new JLabel(" 手机号:");
 76         this.phone.setFont(song);
 77         this.setmessage(DEFAULT);
 78         this.message.setForeground(Color.black);
 79         this.message.setFont(song);
 80         // 创建文本框
 81         this.usernameField = new JTextField();
 82         this.usernameField.setPreferredSize(filedSize);
 83         this.usernameField.setFont(song);
 84         this.phoneField = new JTextField();
 85         this.phoneField.setPreferredSize(filedSize);
 86         this.phoneField.setFont(song);
 87         // 创建密码框
 88         this.passwordField = new JPasswordField();
 89         this.passwordField.setPreferredSize(filedSize);
 90         this.passwordField.setFont(song);
 91         this.confirmPasswordField = new JPasswordField();
 92         this.confirmPasswordField.setPreferredSize(filedSize);
 93         this.confirmPasswordField.setFont(song);
 94         // 创建按钮
 95         this.signup = new JButton("注册");
 96         this.signup.setFont(song);
 97     }
 98 
 99     // 添加组件到窗口的方法
100     private void addComponents() {
101         // 创建第一行面板,添加用户名标签和文本框
102         JPanel panel1 = new JPanel();
103         panel1.add(username);
104         panel1.add(usernameField);
105         // 创建第二行面板,添加密码标签和密码框
106         JPanel panel2 = new JPanel();
107         panel2.add(password);
108         panel2.add(passwordField);
109         // 创建第三行面板,添加再次输入密码标签和密码框
110         JPanel panel3 = new JPanel();
111         panel3.add(confirmPassword);
112         panel3.add(confirmPasswordField);
113         // 创建第四行面板,添加手机号标签和文本框,以及注册按钮和信息标签
114         JPanel panel4 = new JPanel();
115         panel4.add(phone);
116         panel4.add(phoneField);
117 
118         // 将四个面板按顺序添加到窗口
119         this.add(panel1);
120         this.add(panel2);
121         this.add(panel3);
122         this.add(panel4);
123         this.add(message);
124         this.add(signup);
125     }
126     // 监听区
127     public void addListener() {
128         this.signup.addActionListener(this);
129     }
130 
131     @Override
132     public void actionPerformed(ActionEvent e) {
133         String uname = this.usernameField.getText();
134         String pass = this.passwordField.getText();
135         String confrimpass = this.confirmPasswordField.getText();
136         String uphone = this.phoneField.getText();
137         // 检测是否是空的
138         boolean isNull = uname.equals("")||pass.equals("")||confrimpass.equals("");
139         // 检测用户名及密码长度
140         boolean security = (uname.length()<2 || uname.length()>15) || (pass.length()<2 || pass.length()>15);
141         if (isNull || security || uphone.length()>11) {
142             this.setmessage(DEFAULT);
143             JOptionPane.showMessageDialog(this, "用户名及密码为2到15位,电话号码可空,但不能超过11位");
144         } else if (!pass.equals(confrimpass)) {
145             this.setmessage(UNEQUAL_PASS);
146         }else {
147             userInfo user = new userInfo(uname, pass, uphone);
148             // 测试库中是否有相同用户名的,其实可以不用,因为uname有唯一约束
149             int temp = userInfo.queryUser(uname).size();
150             if (temp != 0) {
151                 this.setmessage(EXIST_USER);
152             } else {
153                 userInfo.insertUser(user);
154                 this.dispose();
155             }
156         }
157     }
158 
159     public static void main(String[] args) {
160         // 创建注册界面对象
161         signUp registerFrame = new signUp();
162         registerFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
163     }
164 }

运行结果如下:

注册的提示状态也如上,会使下方提示字变红。

接下来是对上次计算器的更新,对计算代码有一定更新,代码如下:

  1 package POJO;
  2 
  3 
  4 import java.util.*;
  5 import java.math.*;
  6 
  7 
  8 public class calculate {
  9 
 10     // 符号栈
 11     private Stack<String> signStack = new Stack();
 12 
 13     // 数字栈
 14     private Stack<BigDecimal> numStack = new Stack();
 15 
 16 
 17     /*******************************************将军的恩情还不完*************************************************/
 18 
 19 
 20     // 额switch可以判断引用类型变量的值相等吗{0:'+', 1:'-', 2:'x', 3:'/'}
 21     private static final String signCode = "+-x/()";
 22 
 23     // 计算器主体模块
 24     public BigDecimal work(BigDecimal caled, BigDecimal cal, String sign) {
 25         BigDecimal ans = null;
 26         switch (signCode.indexOf(sign)) {
 27             case 0:
 28                 ans = caled.add(cal);
 29                 break;
 30             case 1:
 31                 ans = caled.subtract(cal);
 32                 break;
 33             case 2:
 34                 ans = caled.multiply(cal);
 35                 break;
 36             case 3:
 37                 try {
 38                     ans = caled.divide(cal);
 39                 }catch (ArithmeticException AE) {
 40                     if(AE.getLocalizedMessage().equals("Non-terminating decimal expansion; no exact representable decimal result."))
 41                         ans = caled.divide(cal, 5, BigDecimal.ROUND_HALF_UP);
 42                     else
 43                         ans = BigDecimal.valueOf(32202);
 44                     System.out.println("Exception : "+AE.getLocalizedMessage());
 45                 }
 46                 break;
 47             case 4:
 48             case 5:
 49                 this.numStack.push(caled);
 50                 ans = cal;
 51                 break;
 52             default:
 53                 ans = null;
 54         }
 55         return ans;
 56     }
 57 
 58     // 设计开方(牛顿莱布尼兹)
 59     public static BigDecimal niuton(BigDecimal caled) {
 60         BigDecimal ans;
 61         if (caled.doubleValue() < 0) {
 62             System.out.println("Exception : Negative caled");
 63             return BigDecimal.valueOf(32202);
 64         }
 65         double x = 1;
 66         double y = x - (x * x - caled.doubleValue()) / (2 * x);
 67         while (x - y > 0.00000001 || x - y < -0.00000001) {
 68             x = y;
 69             y = x - (x * x - caled.doubleValue()) / (2 * x);
 70         }
 71         ans = BigDecimal.valueOf(y);
 72         return ans;
 73     }
 74 
 75     // 设计平方
 76     public static BigDecimal square(BigDecimal caled) {
 77         return caled.pow(2);
 78     }
 79 
 80     // 设计清屏
 81     public void refresh() {
 82         this.numStack.clear();
 83         this.signStack.clear();
 84         this.signStack.push("=");
 85         // 解决计算当(x+y)后输入符号时,需要出栈两个数进行括号运算(即将数按顺序压回去)时数字栈只有一个栈的问题。
 86         this.numStack.push(new BigDecimal(0));
 87     }
 88 
 89 
 90     /**********************************************入集中营**************************************************/
 91 
 92     // 索引,见详细设计
 93     private String index = "+-x/()=";
 94 
 95     // 数据,见详细设计^^_  ,>为0,<为1,=为2,null为3
 96     private int[][] compareToSign = {{0, 0, 1, 1, 1, 0, 0}, {0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 1, 0, 0},
 97             {0, 0, 0, 0, 1, 0, 0}, {1, 1, 1, 1, 1, 2, 3}, {0, 0, 0, 0, 3, 0, 0}, {1, 1, 1, 1, 1, 3, 2}};
 98 
 99 
100     // 数字入栈
101     public void numPush(String decimal) {
102         this.numStack.push(new BigDecimal(decimal));
103     }
104 
105     public void numPush(BigDecimal decimal) {
106         this.numStack.push(decimal);
107     }
108 
109 
110     // 控制流,详细见详细设计p1
111     public void calIOC(String topSign) {
112         BigDecimal caled, cal;
113         String temp;
114         temp = this.signStack.peek();
115         switch (this.compareToSign[index.indexOf(temp)][index.indexOf(topSign)]) {
116             case 0:
117                 cal = this.numStack.pop();
118                 caled = this.numStack.pop();
119                 temp = this.signStack.pop();
120                 this.numStack.push(this.work(caled, cal, temp));
121                 this.signStack.push(topSign);
122                 break;
123             case 1:
124                 this.signStack.push(topSign);
125                 break;
126             case 2:
127                 this.signStack.pop();
128                 break;
129             default:
130                 System.out.println("Exception : wrong I/O");
131                 break;
132         }
133     }
134 
135 
136     // 等号入栈
137     public BigDecimal equaIOC() {
138         BigDecimal ans, caled, cal;
139         String topSign;
140         while (!"=".equals(this.signStack.peek())) {
141             topSign = this.signStack.pop();
142             cal = this.numStack.pop();
143             caled = this.numStack.pop();
144             this.numStack.push(this.work(caled, cal, topSign));
145         }
146         ans = this.numStack.pop();
147         return ans;
148     }
149 
150     // pow的IO流控制
151     public void powIOC(String topSign) {
152         BigDecimal temp;
153         temp = this.numStack.pop();
154         if (topSign.equals("^2")) {
155             this.numStack.push(calculate.square(temp));
156         } else {
157             this.numStack.push(calculate.niuton(temp));
158         }
159     }
160 
161     public static void main(String[] args) {
162         calculate c = new calculate();
163         c.numPush("2");
164         c.powIOC("^2");
165         System.out.println(c.numStack.peek());
166     }
167 
168     // 通过循环执行运算功能直到左括号为栈顶符号来规避括号内有运算符
169     public void barcketIOC() {
170         BigDecimal caled, cal;
171         String topSign;
172         while (!"(".equals(this.signStack.peek())) {
173             topSign = this.signStack.pop();
174             cal = this.numStack.pop();
175             caled = this.numStack.pop();
176             this.numStack.push(this.work(caled, cal, topSign));
177         }
178         this.signStack.pop();
179     }
180 
181 
182 }

计算器界面如下,跟新了更多接口,新的计算器界面记录了用户名,被计算式和答案,方便数据插入,代码如下:

package Service;

// 导入自己的计算类
import POJO.calculate;
import VO.record;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.math.BigDecimal;


// 计算器界面
public class calculator extends JFrame implements ActionListener {

    // 获得显示屏大小
    public static final int SCREAM_HEIGHT = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
    public static final int SCREAM_WIDTH = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
    // 根据显示屏高度设定计算器界面大小
    public static final int EXAMPLE = (int) (SCREAM_HEIGHT / 4.32);


    // 字体大小
    Font cu = new Font("粗体", Font.BOLD, (int) (EXAMPLE * 0.2));


    /**********************************************超级初始化块*******************************************************/


    protected void __init__() {
        // 设置窗口名称
        this.setTitle("计算器");
        // 4比3固定窗口
        this.setSize(EXAMPLE * 3, EXAMPLE * 4);
        this.setResizable(false);
        this.setLocationRelativeTo(null);
        // 设置窗口可见
        this.setVisible(true);
        this.setBackground(Color.black);
        // 设置关闭按钮(释放进程)
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        // 设置方向布局
        this.setLayout(new BorderLayout());
    }


    /**********************************************北国风光*******************************************************/

    // 北面组件
    private JPanel northBox = new JPanel(new FlowLayout());
    private JTextField input = new JTextField();
    private JButton clear = new JButton();


    // 设置北面组件
    private void setNorth() {
        // 设置数字栏
        this.input.setPreferredSize(new Dimension((int) (EXAMPLE * 2.2), (int) (EXAMPLE * 0.4)));
        this.input.setFont(this.cu);
        this.input.setForeground(Color.BLACK);      // 额好像没用,但限制用户输入更重要
        this.input.setEnabled(false);
        this.input.setHorizontalAlignment(SwingConstants.RIGHT);

        // 设置清空
        this.clear.setText("C");
        this.clear.setPreferredSize(new Dimension((int) (EXAMPLE * 0.4), (int) (EXAMPLE * 0.4)));
        this.clear.setFont(this.cu);
        this.clear.setForeground(Color.RED);

        // 安装北仪表
        this.northBox.add(this.input);
        this.northBox.add(this.clear);

        // 安装北仪表到主体
        this.add(this.northBox, BorderLayout.NORTH);
    }


    /**********************************************中央处理器*******************************************************/


    // 中部组件
    private JPanel CPU = new JPanel();
    private JButton[] cal = new JButton[20];
    // 后16个按钮顺序,懒得写集合类了
    String str = "789/456x123-0.=+";

    // 设置中部组件
    private void setCenter() {
        // 划分20格
        this.CPU.setLayout(new GridLayout(5, 4));
        // 设置开方按钮
        this.cal[0] = new JButton();
        this.cal[0].setText("^-2");
        this.cal[0].setPreferredSize(new Dimension((int) (EXAMPLE * 0.2), (int) (EXAMPLE * 0.15)));
        this.cal[0].setFont(this.cu);
        this.cal[0].setForeground(Color.BLUE);
        // 设置括号按钮
        this.cal[1] = new JButton();
        this.cal[1].setText("^2");
        this.cal[1].setPreferredSize(new Dimension((int) (EXAMPLE * 0.2), (int) (EXAMPLE * 0.15)));
        this.cal[1].setFont(this.cu);
        this.cal[1].setForeground(Color.BLUE);
        this.cal[2] = new JButton();
        this.cal[2].setText("(");
        this.cal[2].setPreferredSize(new Dimension((int) (EXAMPLE * 0.2), (int) (EXAMPLE * 0.15)));
        this.cal[2].setFont(this.cu);
        this.cal[2].setForeground(Color.BLUE);

        // 设置清除按钮
        this.cal[3] = new JButton();
        this.cal[3].setText(")");
        this.cal[3].setPreferredSize(new Dimension((int) (EXAMPLE * 0.2), (int) (EXAMPLE * 0.15)));
        this.cal[3].setFont(this.cu);
        this.cal[3].setForeground(Color.BLUE);

        // 设置后16个按钮
        for (int i = 4; i < 20; i++) {
            String temp = this.str.substring(i - 4, i - 3);
            this.cal[i] = new JButton();
            this.cal[i].setText(temp);
            this.cal[i].setPreferredSize(new Dimension((int) (EXAMPLE * 0.2), (int) (EXAMPLE * 0.15)));
            this.cal[i].setFont(this.cu);
            if ("+-x/=".contains(temp)) {
                this.cal[i].setForeground(Color.GRAY);
            }
        }
        // 添加按钮
        for (int i = 0; i < 20; i++) {
            this.CPU.add(this.cal[i]);
        }
        this.add(this.CPU,BorderLayout.CENTER);
    }


    /********************************************** 南柯一梦 *******************************************************/

    public String user = "admin";

    public void setUser(String user) {
        this.user = user;
    }

    // 南面组件
    private JLabel message = new JLabel("welcome," + this.user, SwingConstants.CENTER);

    // 设置南面组件
    private void setSouth() {
        this.message.setText("welcome," + this.user);
        this.message.setPreferredSize(new Dimension((int) (EXAMPLE * 0.1), (int) (EXAMPLE * 0.1)));
        this.message.setForeground(Color.BLACK);
        this.add(this.message, BorderLayout.SOUTH);
    }


    /*********************************************监听*********************************************************/

    // 给按钮添加监听
    private void setListener() {
        for (JButton j : cal) {
            j.addActionListener(this);
        }
        this.clear.addActionListener(this);
    }

    // 监听事件设置
    @Override
    public void actionPerformed(ActionEvent e) {
        String listen = e.getActionCommand();
        if ("0.1^23456789+-x/()^-2".contains(listen)) {
            this.input.setText(this.input.getText() + listen);
        }
        this.bigWork(listen);
    }


    /*****************************************状态**************************************************/

    // 小数点信号
    private Boolean pointSignal = false;
    // 括号信号
    private int barcketNum = 0;

    private String num = "0123456789";
    private String sign = "+-x/(";

    // 输入的最后一位为数字时的状态,详细见详细设计表格
    public void inNum() {
        // 只能输入pow函数,右括号,数字和符号按钮,不能输入左括号,若小数点信号为真,则可以输入小数点
        for (int i=0;i<20;i++) {
            if("(".equals(this.cal[i].getText())) {
                this.cal[i].setEnabled(false);
            }
            else {
                this.cal[i].setEnabled(true);
            }
        }
        // 根据信号设置
        this.cal[17].setEnabled(this.pointSignal);
    }

    // 输入的最后一位为符号或左括号时
    public void inSign() {
        // 只能输入非小数点数字及左括号,小数点信号开启
        for (int i=0;i<20;i++) {
            if("(".equals(this.cal[i].getText()) || this.num.contains(this.cal[i].getText())) {
                this.cal[i].setEnabled(true);
            }
            else {
                this.cal[i].setEnabled(false);
            }
        }
        this.pointSignal = true;
    }

    // 输入最后一位为右括号或pow运算时
    public void inPow() {
        // 只能输入符号和右括号和pow函数
        for (int i=0;i<20;i++) {
            if("(".equals(this.cal[i].getText()) || this.num.contains(this.cal[i].getText()) || ".".equals(this.cal[i].getText())) {
                this.cal[i].setEnabled(false);
            }
            else {
                this.cal[i].setEnabled(true);
            }
        }
    }

    // 输入最后一位为小数点时
    public void inPoint() {
        // 只能输入非小数点数字,小数点信号关闭
        for (int i=0;i<20;i++) {
            if(this.num.contains(this.cal[i].getText())) {
                this.cal[i].setEnabled(true);
            }
            else {
                this.cal[i].setEnabled(false);
            }
        }
        this.pointSignal = false;
    }

    public void inEqual() {
        for (int i=0;i<20;i++) {
            this.cal[i].setEnabled(false);
        }
    }


    /*****************************************核酸隔离点*********************************************/


    // 真正的超级初始化块
    public calculator() throws HeadlessException {
        // 界面设置
        this.__init__();
        this.setNorth();
        this.setCenter();
        this.setSouth();
        // 交互设置
        this.setListener();
        JOptionPane.showMessageDialog(this, "由于框架原因,本计算器打开时可能按钮显示不全,请最小化后打开");
        this.inSign();
        this.calculate.refresh();
    }


    calculate calculate = new calculate();
    private String temStr = "";

    // 记录公式及答案为后续拓展提供api
    private String caled;
    private String ans;

    public void setCaled(String caled) {
        this.caled = caled;
    }

    public void setAns(BigDecimal ans) {
        this.ans = ans.toString();
    }

    public String getCaled() {
        return caled;
    }

    public String getAns() {
        return ans;
    }

    // 控制器
    public void bigWork(String listen) {
        
        // 记录括号信号
        if ("(".equals(listen)) {
            this.barcketNum++;
        }
        if (")".equals(listen)) {
            this.barcketNum--;
        }
        
        // 基础状体转换
        if (this.num.contains(listen)) {
            this.temStr = this.temStr +listen;
            this.inNum();
        } else if (this.sign.contains(listen)) {
            if(!"".equals(temStr)) {
                this.calculate.numPush(this.temStr);
                this.temStr = "";
            }
            this.calculate.calIOC(listen);
            this.inSign();
        } else if (")".equals(listen) || listen.contains("^")) {
            if(!"".equals(temStr)) {
                this.calculate.numPush(this.temStr);
                this.temStr = "";
            }
            if (listen.contains("^")) {
                calculate.powIOC(listen);
            } else {
                this.calculate.barcketIOC();
            }
            this.inPow();
        } else if (".".equals(listen)) {
            this.temStr = this.temStr +listen;
            this.inPoint();
        }  else if ("=".equals(listen)) {
            if(!"".equals(temStr)) {
                this.calculate.numPush(this.temStr);
                this.temStr = "";
            }
            this.setCaled(this.input.getText());
            this.setAns(this.calculate.equaIOC());
            // 记录接口
            record re = new record(this.user, this.caled,this.ans);
            record.insertCal(re);
            this.input.setText(re.toString());
            this.inEqual();
        }else if ("C".equals(listen)) {
            this.calculate.refresh();
            this.input.setText("");
            this.temStr = "";
            this.barcketNum = 0;
            this.inSign();
        } else {
            JOptionPane.showMessageDialog(this, "error : unvaild input");
        }
        
        // 限制用户输入
        if (this.barcketNum < 0) {
            JOptionPane.showMessageDialog(this,"error : wrong number of barcket");
        }
        if(this.barcketNum == 0) {
            this.cal[3].setEnabled(false);
        }

        if (this.barcketNum > 0) {
            this.cal[18].setEnabled(false);
        }
    }

    public calculator(String uname) throws HeadlessException{
        // 界面设置
        this.__init__();
        this.setNorth();
        this.setCenter();
        // 获取uname
        this.setUser(uname);
        this.setSouth();
        // 交互设置
        this.setListener();
        JOptionPane.showMessageDialog(this, "由于框架原因,本计算器打开时可能按钮显示不全,请最小化后打开");
        this.inSign();
        this.calculate.refresh();
    }

    // 软件测试
    public static void main(String[] args) {
        calculator cal = new calculator();
        System.out.println(SCREAM_WIDTH + " * " + SCREAM_HEIGHT + " due " + EXAMPLE);
    }


}

现在计算器界面下方的文字是welcome,用户名:

  

四、代码展示

点此下载计算器部分代码,由于篇幅原因,多余代码不再次展示。