《高级Java程序设计》大作业(双号版)
北京信息科技大学2021~2022 学年第2学期
《高级Java程序设计》计时大作业
时间:2022年5月21日星期六 18:30~21:00
注意:此试卷为学号尾号为2,4,6,8,0的学生使用的试卷
班级: 学号: 姓名(电子签名):
大作业注意事项:
- 要求学生在上述指定时间内,按要求独立完成大作业;
- 禁止使用任何网络聊天工具;禁止相互交流讨论,禁止传递任何资料与信息;
- 在规定的时间内上传大作业文档到课堂派;
- 大作业雷同者,大作业成绩按零分处理。
按下列需求,设计与实现一个网络版的银行管理系统的部分模块。
一、数据库设计(15分)
- (2分)创建银行管理数据库,银行管理数据库以Bank+你的姓名的汉语拼音缩写命名(例如:张三的数据库名为BankZS)
- (8分)在银行管理数据库中创建账户数据表,账户数据表以account+你的学号命名,账户数据表中包含账号(accNo,varchar(10),Primary key),账户名(accName,varchar(30)),余额( balance , decimal(7,2) ),开户日期(dateCreated,date)
- (5分)在账户数据表中添加5条记录。
其中一条记录的账号是你的学号,账户名为你的姓名,余额要大于3000
其他4条记录中2条大于5000,2条小于5000
【在此粘贴证明您完成上述任务的截图】
- 规划项目结构(5分),
- 创建工程,工程以你的姓名的全拼音命名(例如,张三的项目名称zhangsan)
- 在项目中创建5个包
server.idao(数据访问接口包)
server.dao(数据访问模型包)
server.model(数据模型包)
server.util(工具包)
client.view(视图包)
【在此粘贴证明您完成上述任务的截图】
- 创建服务器端程序(60分)
- (10分)在server.model包中,创建一个封装了账户信息并实现了序列化接口的类Account ,Account类中应包含accountNo,accountName,balance,dateCreated属性,set/get方法,toString()方法。
【在此粘贴源程序(注意:源程序不能是截图)】
public class Account implements Serializable {
String accNo;
String accName;
Double balance;
Date dateCreated;
public Account() {
}
public Account(String accNo, String accName, Double balance, Date dateCreated) {
this.accNo = accNo;
this.accName = accName;
this.balance = balance;
this.dateCreated = dateCreated;
}
public String getAccNo() {
return accNo;
}
public void setAccNo(String accNo) {
this.accNo = accNo;
}
public String getAccName() {
return accName;
}
public void setAccName(String accName) {
this.accName = accName;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
public Date getDateCreated() {
return dateCreated;
}
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
@Override
public String toString() {
return "Account{" +
"accNo='" + accNo + '\'' +
", accName='" + accName + '\'' +
", balance=" + balance +
", dateCreated=" + dateCreated +
'}';
}
}
- (10分)在server.util包中建立一个SqlHelper类封装对数据库的连接操作和关闭操作。并设计一个测试类TestSqlHelper,测试SqlHelper的使用。
【在此粘贴源程序(注意:源程序不能是截图)】
public class SqlHelper {
public static Connection connect() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/BankCMK?useSSL=true&ServerTimezone=UTC";
Connection conn = DriverManager.getConnection(url, "root", "$Now2022");
return conn;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void closeResultSet(ResultSet rs) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void closePreparedStatement(PreparedStatement ps){
try{
ps.close();
}catch(Exception e){
e.printStackTrace();
}
}
public static void closeConnection(Connection conn){
try{
conn.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
【在此粘贴测试SqlHelper结果的截图(证明数据库连接成功)】
- (6分)在server.idao包中,定义一个账户的数据访问接口IAccountDao,
(b)学号尾号为2,4,6,8.0的学生IAccountDao接口包含的抽象方法如下:
- public int findCount(String accountName); 查询账户名中包含某一指定字符串的账户数量
- boolean withdraw(String accountNo,double money); 从指定账户中取款,账户余额减少money。如果取款成功返回true
- Account findAccount(String accountNo);查询一条指定账户的账户信息。
- List<Account> findAccounts(Date dateCreated1,Date dateCreated2)用于从数据库中查询开户日期在dateCreated1和dateCreated2之间的账户信息。
- 注:Date为java.sql包中的Date
【在此粘贴源程序(注意:源程序不能是截图)】
public interface IAccountDao {
public int findCount(String accountName);
boolean withdraw(String accountNo,double money);
Account findAccount(String accountNo);
List<Account> findAccounts(Date dateCreated1, Date dateCreated2);
}
- (14分)在server.dao包中,定义一个实现了接口IAccountDao的数据访问类AccountDao,并编写测试类。按下列要求具体实现接口IAccountDao提供的功能,并用下列给出的测试类测试。
(b)学号尾号为2,4,6,8,0的学生具体实现:
- public int findCount(String accountName);
- boolean withdraw(String accountNo,double money);
- Account findAccount(String accountNo);
- List<Account> findAccounts(Date dateCreated1,Date dateCreated2)用于从数据库中查询开户日期在dateCreated1和dateCreated2之间的账户信息。
- 注:Date为java.sql包中的Date
【测试程序TestAccountDao.java】
package server.dao;
import java.sql.Date;
import java.util.List;
import server.model.Account;
public class TestAccountDao2 {
public static void main(String[] ages){
List<Account> list=null;
AccountDao accountdao=new AccountDao();
System.out.println("\n查询记录数findCount(String studName)");
System.out.println("账户表中记录数="+accountdao.findCount(""));
System.out.println();
System.out.println("查询账号为你的学号的那条记录.findAccount(accNo)");
String accNo="你的学号";//注意:在这里把你真实的学号赋值给变量accNo
Account acc=accountdao.findAccount(accNo);
System.out.println(acc);
System.out.println();
System.out.println("\n取款300");
System.out.println("取款成功:"+accountdao.withdraw(accNo, 300));
System.out.println("\n测试查询多条记录findAccounts(studName");
list=accountdao.findAccounts("");
for(Account s:list)
System.out.println(s);
System.out.println();
System.out.println("\n测试查找账户开户日期在2022年的账户 ");
Date date1=new Date(2022-1900,0,0);
Date date2=new Date(2022-1900,11,30);
list=accountdao.findAccounts(date1, date2);
for(Account s:list)
System.out.println(s);
System.out.println();
}
}
【在此粘贴源程序AccountDao.java(注意:源程序不能是截图)】
public class AccountDao implements IAccountDao {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
@Override
public int findCount(String accountName) {
int count = 0;
try {
conn = SqlHelper.connect();
String sql = "Select count(*) from accountCMK where accName like ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "%" + accountName + "%");
rs = ps.executeQuery();
if (rs.next()) {
// 因为所查询出来的数据,也只有 count 一行而已。
count = rs.getInt(1);
}
} catch (Exception e) {
} finally {
SqlHelper.closeResultSet(rs);
SqlHelper.closePreparedStatement(ps);
SqlHelper.closeConnection(conn);
}
return count;
}
@Override
public boolean withdraw(String accountNo, double money) {
try {
conn = SqlHelper.connect();
String sql = "update accountCMK set balance=balance-? where accNo=?";
ps = conn.prepareStatement(sql);
ps.setDouble(1, money);
ps.setString(2, accountNo);
int n = ps.executeUpdate();
return n > 0;
} catch (Exception e) {
} finally {
SqlHelper.closeResultSet(rs);
SqlHelper.closePreparedStatement(ps);
SqlHelper.closeConnection(conn);
}
return false;
}
@Override
public Account findAccount(String accountNo) {
Account account = null;
try {
conn = SqlHelper.connect();
String sql = "Select * from accountCMK where accNo=?";
ps = conn.prepareStatement(sql);
ps.setString(1, accountNo);
rs = ps.executeQuery();
if (rs.next()) {
account = new Account();
account.setAccNo(rs.getString(1));
account.setAccName(rs.getString(2));
account.setBalance(rs.getDouble(3));
account.setDateCreated(rs.getDate(4));
}
} catch (Exception e) {
} finally {
SqlHelper.closeResultSet(rs);
SqlHelper.closePreparedStatement(ps);
SqlHelper.closeConnection(conn);
}
return account;
}
@Override
public List<Account> findAccounts(Date dateCreated1, Date dateCreated2) {
List<Account> list = new ArrayList<>();
try {
conn = SqlHelper.connect();
String sql = "Select * from accountCMK where dateCreated between ? and ?";
ps = conn.prepareStatement(sql);
ps.setDate(1, dateCreated1);
ps.setDate(2, dateCreated2);
rs = ps.executeQuery();
Account account = null;
while (rs.next()) {
account = new Account();
account.setAccNo(rs.getString(1));
account.setAccName(rs.getString(2));
account.setBalance(rs.getDouble(3));
account.setDateCreated(rs.getDate(4));
list.add(account);
}
} catch (Exception e) {
} finally {
SqlHelper.closeResultSet(rs);
SqlHelper.closePreparedStatement(ps);
SqlHelper.closeConnection(conn);
}
return list;
}
}
【在此粘贴测试结果的截图】
- (20分)在server.view包中,创建多线程的服务器(Server)端主程序。
在这个程序中,服务器在某个端口上倾听,等待与客户端连接,同时构造一个线程类,准备接管会话。当一个Socket会话产生后,将这个会话交给线程处理,然后主程序继续监听。
(b)学号尾号为2、4、6的学生设计:当服务器收到客户端发来的一个账号后(例如;2020100012),调用AccountDao对象的 findAccount(String AccountNo)方法,在账户表中查询该账户信息,如果该账户存在就把查询到的账户信息返回给客户端;如果没有查询到该账户,就向客户端返回“该账户不存在”,如果客户端发来的是“exit”,则关闭和该客户端连接。
【在此粘贴源程序(注意:源程序不能是截图)】
public class Server extends JFrame {
private JPanel panUp = new JPanel();
private JPanel panMid = new JPanel();
private JLabel lblLocalPort = new JLabel("本机服务器监听端口:");
protected JButton butStart = new JButton("启动服务器");
protected JTextField tfLocalPort = new JTextField(25);
protected JTextArea taMsg = new JTextArea(25, 25);
JScrollPane scroll = new JScrollPane(taMsg);
/***/
public static int localPort = 8000; // 默认端口 8000
ServerSocket serverSocket; // 服务器端 Socket
AccountDao dao;
Account account=null;
public Server() {
init();
}
private void init() {
panUp.add(lblLocalPort);
panUp.add(tfLocalPort);
panUp.add(butStart);
tfLocalPort.setText(String.valueOf(localPort));
butStart.addActionListener(new startServerHandler()); // 注册 "启动服务器" 按钮点击事件
// panMid 区域初始化
panMid.setBorder(new TitledBorder("监听消息"));
panMid.add(scroll);
taMsg.setEditable(false);
this.setTitle("服务器端");
this.add(panUp, BorderLayout.NORTH);
this.add(panMid, BorderLayout.CENTER);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(600, 300));
this.pack();
this.setVisible(true);
}
private class startServerHandler implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
try {
// 当点击按钮时,获取端口设置并启动新进程、监听端口
localPort = Integer.parseInt(tfLocalPort.getText());
serverSocket = new ServerSocket(localPort);
Thread acptThrd = new Thread(new AcceptRunnable());
acptThrd.start();
taMsg.append("**** 服务器(端口" + localPort + ")已启动 ****\n");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
// TODO 接受用户连接请求的线程关联类
private class AcceptRunnable implements Runnable {
public void run() {
// 持续监听端口,当有新用户连接时 再开启新进程
while (true) {
try {
Socket socket = serverSocket.accept();
// 新的用户已连接,创建 Client 对象
taMsg.append("新用户连接:" + socket.toString()+"\n");
Client client = new Client(socket);
Thread clientThread = new Thread(client);
clientThread.start();
} catch (Exception ex) {
System.out.println(ex);
}
}
}
// TODO 服务器存放用户对象的客户类(主要编程)。每当有新的用户连接时,该类都会被调用
// TODO 该类继承自 Runnable,内部含有 run()方法
private class Client implements Runnable {
private Socket socket; // 用来保存用户的连接对象
private BufferedReader in; // IO 流
private PrintStream out;
// Client类的构建方法。当有 新用户 连接时会被调用
public Client(Socket socket) throws Exception {
this.socket = socket;
InputStream is = socket.getInputStream();
in = new BufferedReader(new InputStreamReader(is));
OutputStream os = socket.getOutputStream();
out = new PrintStream(os);
}
//客户类线程运行方法
public void run() {
try {
while (true) {
String usermsg = in.readLine(); //读用户发来消息
// 如果用户发过来的消息不为空
if (usermsg != null && usermsg.length() > 0) {
if (usermsg.equals("exit")) {
taMsg.append("用户离开~\n");
break;
}
dao=new AccountDao();
account = dao.findAccount(usermsg);
if (account!=null) {
out.println(account);
} else {
out.println("该账户不存在");
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new Server();
}
}
【在此粘贴服务器端运行结果的截图(注意:客户端完成后,再把服务器端的运行结果的截图在此粘贴)】
(d)学号尾号为8和0的学生设计:当服务器收到客户端发来的一个账户名中包含的字符串时(例如,李),调用AccountDao对象的 findCount(String accountName)方法在账户表中查询所有账户名中包含给定字符串的账户数量,并把查询到的结果返回给客户端,如果客户端发来的账户名是“exit”,则关闭和该客户端连接。
【在此粘贴源程序(注意:源程序不能是截图)】
【在此粘贴服务器端运行结果的截图(注意:客户端程序完成后,再把服务器端的运行结果的截图在此粘贴)】
三、创建客户端程序(20分)
- 在client.view包中创建客户(Client)端主程序。
- 学号尾号为2、4、6的学生设计:客户端输入要查询的账号,然后利用套接字向服务器发送该账号,接受服务器返回的查询结果,并输出查询结果(如果查询到了满足条件的账号,就把查询到的账号信息显示出来,否则输出“该账号不存在”)。
【在此粘贴源程序(注意:源程序不能是截图)】
public class Client extends JFrame {
private JPanel mainPan = new JPanel();
private JLabel lbl_1 = new JLabel("查询账号:");
protected JTextField tf_1 = new JTextField(10);
protected JTextArea taMsg = new JTextArea(25, 25);
protected JButton butStart = new JButton("查询");
/***/
BufferedReader in;
PrintStream out;
public static int localPort = 8000;
public static String localIP = "127.0.0.1";
public Socket socket;
Account account;
String msg;
public Client() {
init();
}
private void init() {
mainPan.setLayout(new FlowLayout());
mainPan.add(lbl_1);
mainPan.add(tf_1);
mainPan.add(butStart);
mainPan.add(taMsg);
butStart.addActionListener(new btnHandlerStart());
this.setTitle("客户端");
this.add(mainPan, BorderLayout.CENTER);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(400, 400));
this.pack();
this.setVisible(true);
}
private class btnHandlerStart implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// 当点击"连接服务器"按钮之后,该按钮被禁用(不可重复点击)。同时"断开服务器按钮"被恢复使用
linkServer(); // 连接服务器
Thread acceptThread = new Thread(new ReceiveRunnable());
acceptThread.start();
}
}
// TODO 连接服务器的方法
public void linkServer() {
try {
socket = new Socket(localIP, localPort);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// TODO 接收服务器消息的线程关联类
private class ReceiveRunnable implements Runnable {
public void run() {
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintStream(socket.getOutputStream());
out.println(tf_1.getText());
out.flush();
while (true) {
msg = in.readLine();
taMsg.setText(msg);
}
} catch (Exception e) {
}
}
}
// TODO 窗口关闭的动作事件监听处理类
// 当用户点击 "x" 离开窗口时,也会向服务器发送 bye 请求,目的是为了同步更新数据。
private class WindowHandler extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
out.println("exit");
}
}
public static void main(String[] args) {
new Client();
}
}
【在此粘贴客户端运行结果的截图(注意:服务器端程序完成后,再把客户端的运行结果的截图在此粘贴)截图要有两种查询结果,即查询到满足条件记录的结果和不满足条件的结果】
- 学号尾号为8,0的学生设计:客户端输入要查询的账户名所包含的字符串,然后利用套接字向服务器发送该字符串,接受服务器返回的查询结果,并输出查询结果)。
【在此粘贴源程序(注意:源程序不能是截图)】
【在此粘贴客户端运行结果的截图(注意:服务器端程序完成后,再把客户端的运行结果的截图在此粘贴)截图要有两种查询结果,查询到的满足条件的记录数要大于0】

浙公网安备 33010602011771号