一、前言
本次作业是基于上次的计算器功能所做的改进,通过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,用户名:
四、代码展示
点此下载计算器部分代码,由于篇幅原因,多余代码不再次展示。
标签:JDBC,String,int,EXAMPLE,uname,swing,new,JavaSE,public From: https://www.cnblogs.com/Lzufe-aquarrr/p/17860562.html