Java - 快递管理系统进阶 (网络编程, 客户端与服务器端, 服务器端与本地之间的数据交互)

it2025-11-11  9

文章目录

1. 任务要求2. 代码分层3. 关键代码3.1 ExpressDao类(持久层操作类)3.2 Client客户端类3.3 Server服务器类3.4 Tools类 (与本地数据进行交互)3.5 Views 视图层3.6 Express实体类 4. 总结

1. 任务要求

基于之前的快递管理系统对其进行重构从而实现数据存储于客户端的分离

2. 代码分层

3. 关键代码

3.1 ExpressDao类(持久层操作类)

package com.java.Express12.dao; import com.java.Express12.bean.Express; import com.java.Express12.bean.Location; import com.java.Express12.util.Tools; import java.io.IOException; import java.util.ArrayList; import java.util.Random; public class ExpressDao { private ArrayList<Express> expressList = new ArrayList<Express>(); private Random random = new Random(); private int size = 0; private static Tools tool = new Tools(); public void setExpressList(ArrayList<Express> expressList) { this.expressList = expressList; } /** * 添加存储快递 * * @param e * @return */ public boolean add(Express e) throws IOException { //判断快递柜是否满了 if (size == 100) { return false; } //生成x,y位置, x = random.nextInt(10) (0~9 随机生成) int x = -1; int y = -1; //比较该位置无快递才能存入, 否则不结束循环(不断的重新生成) while (true) { x = random.nextInt(10); y = random.nextInt(10); if (!(expressList.contains(x) && expressList.contains(y))) { break; } } Location l = new Location(); l.setX(x); l.setY(y); e.setLocation(l); expressList.add(e); //生成取件码 (遍历取件码) int code = randomCode(); e.setCode(code); tool.storeExpress(expressList); return true; } /** * 模板方法设计模式 => 知道返回结果就先写个方法 * * @return */ private int randomCode() { //100000~999999 => 100000 +899999 while (true) { int code = random.nextInt(900000) + 100000; Express e = findByCode(code); if (e == null) { return code; } } } public Express findByNumber(String number) { Express e = new Express(); e.setNumber(number); for (Express express : expressList) { String num = express.getNumber(); if (num.equals(number)) { return express; } } return null; } public Express findByCode(int code) { for (Express express : expressList) { int code1 = express.getCode(); if (code == code1) { return express; } } return null; } public void update(Express oldExpress, Express newExpress) throws IOException { delete(oldExpress); add(newExpress); tool.storeExpress(expressList); } public void delete(Express e) throws IOException { for (Express express : expressList) { if (e.equals(express)) { expressList.remove(express); size--; break; } } tool.storeExpress(expressList); } public ArrayList<Express> findAll() throws IOException { return tool.loadExpress(); } }

3.2 Client客户端类

package com.java.Express12.cloud; import com.java.Express12.bean.Express; import com.java.Express12.view.Views; import java.io.*; import java.net.Socket; import java.util.ArrayList; import java.util.Scanner; /** * @author: Aleo阿乐 * @date: 2020/10/21 * @time: 15:40 */ public class Client { /** * 声明视图类, 确保客户端尽量不涉及持久层操作(虽然有些还是偷了点懒) */ public static Views v = new Views(); public static void main(String[] args) throws IOException, ClassNotFoundException { Scanner input = new Scanner(System.in); // 传入本机地址和端口 Socket socket = new Socket("127.0.0.1", 52101); // 向服务器发送请求 System.out.println("正在向服务器发送请求"); //建立 输/入出流 OutputStream os = socket.getOutputStream(); InputStream is = socket.getInputStream(); ObjectOutputStream oos = new ObjectOutputStream(os); ObjectInputStream ois = new ObjectInputStream(is); PrintStream ps = new PrintStream(os); BufferedReader br = new BufferedReader(new InputStreamReader(is)); w1: while (true) { int num = v.courierMenu(); // 将序号发给服务器 os.write(num); switch (num) { //结束程序 case 0: break w1; // 录入快递 case 1: { oos.writeObject(v.insert()); String text = br.readLine(); System.out.println(text); break; } // 取出快递 case 2: { System.out.println("输入取件码"); // 将取件码传给服务器 ps.println(input.nextLine()); // 读取服务器传过来的快递信息 String text = br.readLine(); if (text == null) { System.out.println("快递不存在, 请检查输入!"); } else { System.out.println(text); System.out.println("成功取出快递"); } break; } case 3: { //查看所有快递 ArrayList<Express> list = new ArrayList<Express>(); list = (ArrayList<Express>) ois.readObject(); // 遍历 for (Express e : list) { System.out.println(e.toString()); } break; } case 4: { System.out.println("请输入需要删除快递的单号:"); String text = input.nextLine(); ps.println(text); Express e = (Express) ois.readObject(); if (e == null) { String s = br.readLine(); System.out.println(s); break; } else { System.out.println("快递被成功删除~"); break; } } case 5: { System.out.println("请输入需要修改的快递单号:"); String text = input.nextLine(); ps.println(text); Express e2 = (Express) ois.readObject(); if (e2 == null) { System.out.println("快递不存在请重新操作"); } else { System.out.println("请输入新的快递单号: "); String number = input.nextLine(); System.out.println("请输入新的快递公司: "); String company = input.nextLine(); e2.setNumber(number); e2.setCompany(company); oos.writeObject(e2); } //这里偷了一个懒, 没有在服务器端进行操作 String s = br.readLine(); if (s != null) { System.out.println(s); v.printExpress(e2); } break; } case 6: { String s = br.readLine(); System.out.println(s); break; } default: break; } } // 在最后关闭所有连接 os.close(); is.close(); oos.close(); ois.close(); ps.close(); br.close(); } }

3.3 Server服务器类

package com.java.Express12.cloud; import com.java.Express12.bean.Express; import com.java.Express12.dao.ExpressDao; import com.java.Express12.util.Tools; import com.java.Express12.view.Views; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; /** * @author: Aleo阿乐 * @date: 2020/10/21 * @time: 15:40 */ public class Server { public static Views v = new Views(); public static ExpressDao dao = new ExpressDao(); public static Tools tool = new Tools(); public static void main(String[] args) throws IOException, ClassNotFoundException { ServerSocket server = new ServerSocket(52101); System.out.println("等待客户端连接"); Socket socket = server.accept(); System.out.println("一个客户端连接成功"); //通过socket类获取输入字节流 InputStream is = socket.getInputStream(); //字节流 => 字符流 InputStreamReader isr = new InputStreamReader(is); //读取对象流 ObjectInputStream ois = new ObjectInputStream(is); //逐行读取流 BufferedReader br = new BufferedReader(isr); //通过socket类获取输出字节流 OutputStream os = socket.getOutputStream(); //字节流转换为打印流 PrintStream ps = new PrintStream(os); //输出对象流 ObjectOutputStream oos = new ObjectOutputStream(os); //必须在服务器端进行导入, 所以客户端不能有dao层的操作, 否则list就会不同 ArrayList<Express> list = tool.loadExpress(); dao.setExpressList(list); w1: while (true) { // 接收客户端的请求 int num = is.read(); // 功能选择 switch (num) { case 0: { // 服务器关闭 if ("程序结束".equals(br.readLine())) { // 重新等待其他客户端请求连接 break w1; } } case 1: { // 接收到一个类的快递信息, 进行处理 Express e1 = (Express) ois.readObject(); boolean flag = dao.add(e1); if (flag) { ps.println("服务器: 添加成功"); } else { ps.println("服务器: 添加失败"); } break; } case 2: { // 读取客户端发出的String类型的取件码,读取其中的数字 int code = Integer.parseInt(br.readLine()); // 取出快递信息 Express e = dao.findByCode(code); if (e == null) { String text = null; ps.println(text); } else { String text = e.toString(); ps.println(text); v.success(); dao.delete(e); } break; } case 3: { //必须要和本地化接轨才能保证对数据删除修改之后不出错 oos.writeObject(dao.findAll()); break; } case 4: { String text = br.readLine(); Express e = dao.findByNumber(text); oos.writeObject(e); if (e == null) { ps.println("快递不存在, 请重新输入!"); break; } else { dao.delete(e); break; } } case 5: { String text = br.readLine(); Express e = dao.findByNumber(text); oos.writeObject(e); Express e2 = (Express) ois.readObject(); dao.update(e, e2); String s = "操作成功"; ps.println(s); } case 6: { ArrayList<Express> expressList = dao.findAll(); tool.storeExpress(expressList); String s = "保存数据成功,并且重新载入数据"; ps.println(s); ArrayList<Express> list1 = tool.loadExpress(); dao.setExpressList(list1); break; } } } is.close(); isr.close(); ois.close(); br.close(); os.close(); ps.close(); oos.close(); } }

3.4 Tools类 (与本地数据进行交互)

package com.java.Express12.util; import com.java.Express12.bean.Express; import java.io.*; import java.util.ArrayList; public class Tools { public void storeExpress(ArrayList<Express> expressList) throws IOException { FileOutputStream fos = new FileOutputStream("D://networkExpress.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(expressList); oos.close(); fos.close(); System.out.println("保存快递数据到本地成功"); } public ArrayList<Express> loadExpress() throws IOException { ArrayList<Express> expressList = new ArrayList<>(); try { FileInputStream fis = new FileInputStream("D://networkExpress.txt"); ObjectInputStream ois = new ObjectInputStream(fis); try (fis; ois) { expressList = (ArrayList<Express>) ois.readObject(); System.out.println("载入数据成功"); } catch (IOException | ClassNotFoundException e) { System.out.println("载入数据失败"); } return expressList; } catch (FileNotFoundException fileNotFoundException) { System.out.println("未找到文件"); return expressList; } catch (EOFException eofException) { System.out.println("未存储数据"); return expressList; } } }

3.5 Views 视图层

package com.java.Express12.view; import com.java.Express12.bean.Express; import com.java.Express12.bean.Location; import java.util.ArrayList; import java.util.Scanner; public class Views { private Scanner input = new Scanner(System.in); /** * 快递员视图 */ public int courierMenu() { System.out.println("请根据提示, 输入功能序号: "); System.out.println("0. 退出"); System.out.println("1. 快递录入"); System.out.println("2. 取出快递"); System.out.println("3. 查看所有快递"); System.out.println("4. 快递删除"); System.out.println("5. 快递修改"); System.out.println("6. 保存快递数据到本地且重新载入数据"); //nextLine, 接收一行, 所以包容性很大 (而且nextLine可以接收空格) 只能通过此方法接收空格, 全局都只用nextLine方法, 然后再对其进行处理 String text = input.nextLine(); int num = -1; //因为有可能发生异常将其分为两行, 设置一个默认值, 虽然我觉得好像可以写在一起, 但可能是为了更好的确定异常范围叭~ try { num = Integer.parseInt(text); } catch (NumberFormatException e) { e.printStackTrace(); } if (num < 0 || num > 6) { System.out.println("请输入以上数字哦!!"); return courierMenu(); } return num; } /** * 快递录入 * * @return */ public Express insert() { System.out.println("添加快递, 请输入快递信息: "); System.out.println("请输入快递单号: "); String number = input.nextLine(); System.out.println("请输入快递公司: "); String company = input.nextLine(); Express e = new Express(); e.setNumber(number); e.setCompany(company); return e; } public void printExpress(Express e) { System.out.println("快递信息如下:"); System.out.println("快递公司: " + e.getCompany() + ", 快递单号: " + e.getNumber() + ", 取件码: " + e.getCode()); } /** * 更新快递信息的操作, 输入新的快递信息, 直接对已知对象进行操作 * * @param e */ public void update(Express e) { System.out.println("请输入新的快递单号: "); String number = input.nextLine(); System.out.println("请输入新的快递公司: "); String company = input.nextLine(); e.setNumber(number); e.setCompany(company); } public void printAll(ArrayList<Express> es) { int count = 0; for (Express express : es) { if (express != null) { count++; Location lo = express.getLocation(); System.out.println("第" + (lo.getX() + 1) + "排" + (lo.getY() + 1) + "列快递"); printExpress(express); } } if (count == 0) { System.out.println("暂无快递信息~"); } } public void success() { System.out.println("操作成功"); } }

3.6 Express实体类

package com.java.Express12.bean; import java.io.Serializable; import java.util.Objects; public class Express implements Serializable { private String number; private String company; private int code; private Location location; public Location getLocation() { return location; } public void setLocation(Location location) { this.location = location; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Express express = (Express) o; return number.equals(express.number); } @Override public int hashCode() { return Objects.hash(number); } @Override public String toString() { return "Express{" + "number='" + number + '\'' + ", company='" + company + '\'' + ", code=" + code + ", location=" + location + '}'; } public Express() { } public Express(String number, String company, int code) { this.number = number; this.company = company; this.code = code; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } }

4. 总结

中途遇到了很多难题, 而且由于粗心大意, 会报很多bug

如:

客户端和服务端的IO流操作一定要一来一回, 你输出他就要接收

有一个bug, 每次选择查看所有快递功能后, 虽然我在dao层的最后都进行了本地数据的存储, 但是直接去查看所有快递的时候仍然会出问题, 后面发现需要对查看所有快递直接对接本地数据读取才行

修改前 => //这里返回的是一直静态存在于dao类里面的expressList, 因此会报出输出流的错误 public ArrayList<Express> findAll() throws IOException { return expressList; } 修改后 => public ArrayList<Express> findAll() throws IOException { return tool.loadExpress(); } //这样就可以保证对任何数据进行增删改查之后都可以正常查看而不是报错了

原因是查看所有快递查看的依然是dao里没有变的ExpressList, 除非在查看的时候就应该重新从本地数据中读入

删除功能需要已删除完就对本地数据进行修改, 因为是建立在服务器上的, 所以每一次的操作都和本地数据的存取息息相关~

一开始添加了6号功能将数据保存至本地并刷新的功能, 发现后期服务端和本地数据的各种交互bug, 因此几乎在dao层的每一个方法的结尾都进行了对本地数据的存储和修改, 因此, 6号功能也就名存实亡了, 就放在这不删当作给自己的警醒和思考了

网络编程的学习让我知道最不方便的就是服务端数据与本地数据的交互, 数据传输极为麻烦且冗余代码过多, 相信后期通过将数据层的代码提取出来后会极大的简化这一部分的开发

若需要连接多个客户端直接将代码放至Thread类中重写run()方法即可

大家加油 !! 网络编程看的就是细心呀!!

源代码下载

最新回复(0)