快递综合-网络编程(包含的知识点 常用类库、集合、IO、多线程、网络编程) 作者:1222-张振亚
下面展示不同模块的代码: 快递源码
。
- 模块1 快递类
- 模块2 视图类
- 模块3 存储控制类
- 模块4 客户端
- 模块5 服务器
1.快递类
package Chapter4_Homework05.bean;
import java.io.Serializable;
import java.util.Objects;
/**
* @program: 新职课作业代码
* @description: 快递类
* @author: 张振亚
* @create: 2021-01-13 23:21
**/
public class Express implements Serializable {
private String number; // 快递单号
private String company; // 公司
private int code; // 取件码
public int posX, posY; // 快递所在快递柜中的位置
// 构造方法
public Express(String number, String company, int code) {
this.number = number;
this.company = company;
this.code = code;
}
public Express() {
}
// getter/setter
public String getNumber() {
return number;
}
public String getCompany() {
return company;
}
public int getCode() {
return code;
}
public void setNumber(String number) {
this.number = number;
}
public void setCompany(String company) {
this.company = company;
}
public void setCode(int code) {
this.code = code;
}
// 重写toString 方法
@Override
public String toString() {
return "Express{" +
"number='" + number + '\'' +
", company='" + company + '\'' +
", code=" + code +
'}';
}
// 重写equals方法
/**
* 只要快递单号相同就认为快递相同
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Express express = (Express) o;
return Objects.equals(number, express.number);
}
@Override
public int hashCode() {
return Objects.hash(code);
}
}
2.视图类
package Chapter4_Homework05.view;
import Chapter4_Homework05.bean.Express;
import javax.print.DocFlavor;
import java.util.Collection;
import java.util.Scanner;
/**
* @program: 新职课作业代码
* @description: 视图类
* @author: 张振亚
* @create: 2021-01-13 23:22
**/
/**
* 视图层
* 只负责展示视图 不包含其他任何逻辑
*/
public class View {
public Scanner input = new Scanner(System.in);
/**
* 获得用户的角色选择输入,并进入相应的功能
*
* @return 返回功能码 1:管理原 2:普通用户 0:退出
*/
public int menu() {
System.out.println("根据提示输入功能序号:");
System.out.println("1,管理员");
System.out.println("2,普通用户");
System.out.println("0,退出");
String s = input.nextLine();
int funcNum = -1;
try {
funcNum = Integer.parseInt(s);
} catch (NumberFormatException e) { // 格式异常 递归继续获取功能码
return menu();
}
if (funcNum < 0 || funcNum > 2) { // 功能码不合法
return menu();
}
return funcNum;
}
/*
-----------------------------------------------------------------
*/
/**
* 获得管理员输入的功能码
*
* @return 管理员输入的合法功能码 1:录入 2:修改 3:删除 4:查看所有 0:退出
*/
public int gMenu() {
System.out.println("根据提示输入功能序号:");
System.out.println("1,快递录入");
System.out.println("2,快递修改");
System.out.println("3,快递删除");
System.out.println("4,查看所有快递");
System.out.println("0,退出");
String s = input.nextLine();
int funcNum = -1;
try {
funcNum = Integer.parseInt(s);
} catch (NumberFormatException e) { // 格式异常 递归继续获取功能码
return gMenu();
}
if (funcNum < 0 || funcNum > 4) { // 功能码不合法
return gMenu();
}
return funcNum;
}
/**
* 1快递员录入信息
*
* @return 返回包含了快递单号和快递公司的快递对象
*/
public Express insert() {
System.out.println("请根据提示输入快递信息:");
System.out.print("请输入快递单号:");
String number = input.nextLine();
System.out.print("请输入快递公司:");
String company = input.nextLine();
Express e = new Express();
e.setNumber(number);
e.setCompany(company);
return e;
}
/**
* 2修改快递信息
*
* @param
*/
public Express update() {
System.out.print("请输入新的快递单号:");
String number = input.nextLine();
System.out.print("请输入新的快递公司");
String company = input.nextLine();
Express eNew = new Express();
eNew.setNumber(number);
eNew.setCompany(company);
// eNew.setCode(e.getCode());
// eNew.posX = e.posX; // 保留快递柜存放快递的位置
// eNew.posY = e.posY;
return eNew;
}
/**
* 3询问是否删除
*
* @return 给出快递管理员的选择 1:删除 2:取消
*/
public int delete() {
System.out.println("确认是否删除:");
System.out.println("1,确认删除");
System.out.println("2,取消删除");
System.out.println("0,退出");
String s = input.nextLine();
int num = -1;
try {
num = Integer.parseInt(s);
} catch (NumberFormatException e) {
return delete();
}
if (num < 0 || num > 2) {
return delete();
}
return num;
}
/**
* 4遍历显示所有快递信息
*
* @param es
*/
public void printAll(Collection<Express> es) {
int count = 0;
for (Express e : es) {
count++;
System.out.print("第" + (e.posX + 1) + "排," + (e.posY + 1) + "列, ");
printExpress(e);
}
if (count == 0) {
System.out.println("暂无快递信息");
}
}
/**
* 提示用户输入快递单号
*
* @return
*/
public String findByNumber() {
System.out.println("请根据提示输入快递信息:");
System.out.print("请输入需要操作的快递单号:");
String number = input.nextLine();
return number;
}
/**
* 显示快递信息
*
* @param e
*/
public void printExpress(Express e) {
if (e == null) {
System.out.println("快递信息不存在");
return;
}
System.out.println("快递信息如下:");
System.out.println("快递公司:" + e.getCompany() + ",快递单号:" + e.getNumber() + ",取件码:" + e.getCode());
}
/*
-----------------------------------------------------------------
*/
/**
* 获得用户输入的取件码(这里简化,只要取件码相同,就算取件成功)
*
* @return 用户输入的合法功能码(6位)
*/
public int uMenu() {
System.out.println("根据提示进行取件:");
System.out.print("请输入取件码:");
String s = input.nextLine();
int funcNum = -1;
try {
funcNum = Integer.parseInt(s);
} catch (NumberFormatException e) { // 格式异常 递归继续获取功能码
return uMenu();
}
if (funcNum < 100000 || funcNum > 999999) { // 功能码不合法
System.out.println("输入有误,请重试!");
return uMenu();
}
return funcNum;
}
public void expressExist() {
System.out.println("此快递单号已存在,请勿重复存储");
}
public void printCode(Express e) {
System.out.println("新快递的取件码为:" + e.getCode());
}
public void success() {
System.out.println("操作成功!");
}
public void printNull() {
System.out.println("快递不存在,请检查输入");
}
}
3.存储控制类
package Chapter4_Homework05.dao;
import Chapter4_Homework05.bean.Express;
import java.io.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Random;
/**
* @program: 新职课作业代码
* @description: 快递存储处理操作类
* @author: 张振亚
* @create: 2021-01-13 23:21
**/
// 实现可序列化标记接口 使得dao对象支持序列化与反序列化
public class ExpressDao {
private Random random = new Random(); // 用于生成随机数
/**
*
* @param e 新加入的快递对象
* @param cabinet 标记快递柜位置是否已被占用
* @param data 所有的快递对象
* @return
*/
public Express add(Express e, boolean[][] cabinet, HashMap<Integer, Express> data){
int size = data.size();
if(size >= 100){
return null;
}
// 1,随机生成两个0-9的下标
int x = -1, y = -1;
while (true){
x = random.nextInt(10);
y = random.nextInt(10);
if(cabinet[x][y] == false){
break; // 此位置未被占用
}
}
// 2,判断取件码是否重复(最简单的 一个个对比)
int code = randomCode(data.values()); // 获得没有重复的取件码
e.setCode(code);
e.posX = x; // 快递柜存放快递的位置
e.posY = y;
size++; // 快递数目加一
cabinet[x][y] = true; // 此位置已被占用
data.put(code, e); // 添加键值对
return e;
}
/**
* 遍历所有对象 生成独一无二的取件码
* @param expresses 所有的快递对象
* @return
*/
private int randomCode(Collection<Express> expresses){
while (true) {
int code = random.nextInt(900000) + 100000; // 范围(000000-899999)+1000000
Express e = findByCode(code, expresses);
if(e == null) { // 说明取件码未重复
return code;
}
}
}
/**
* 快递员根据快递单号查询HashMap中存放的快递
* @param number
* @return
*/
public Express findByNumber(String number, Collection<Express> expresses){
// 遍历HashMap中的Express对象 找到其快递单号相对应的那个对象并返回
for(Express e : expresses) {
if(e.getNumber().equals(number)) {
return e;
}
}
return null;
}
/**
* 根据取件码查询快递
* @param code 取件码
* @return 查询到结果 查询失败返回null
*/
public Express findByCode(int code, Collection<Express> expresses){
for(Express e : expresses) {
if(e.getCode() == code) {
return e;
}
}
return null;
}
/**
* 多余的操作 为了MVC更圆润
* @param oldExpress
* @param newExpress
*/
public Express update(Express oldExpress, Express newExpress, boolean[][] cabinet, HashMap<Integer, Express> data){
newExpress.setCode(oldExpress.getCode());
newExpress.posX = oldExpress.posX;
newExpress.posY = oldExpress.posY;
delete(oldExpress, cabinet, data);
cabinet[newExpress.posX][newExpress.posY] = true; // 此位置已被占用
data.put(newExpress.getCode(), newExpress); // 添加键值对
return newExpress;
}
public void delete(Express e, boolean[][] cabinet, HashMap<Integer, Express> data){
data.remove(e.getCode());
cabinet[e.posX][e.posY] = false;
}
public HashMap<Integer, Express> load() throws IOException {
HashMap<Integer, Express> data = new HashMap<>();
try {
FileInputStream fis = new FileInputStream("cat.txt"); //这里的路径不能写其他项目序列化出来的文件!!!!!!!!!!!!!!
ObjectInputStream ois = new ObjectInputStream(fis);
try (fis;ois) {
data = (HashMap<Integer, Express>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
data = new HashMap<>(); // 打开文件异常时 将data初始为空 在main函数结束时进行序列化
}
}catch (Exception e){
data = new HashMap<>(); // 打开文件异常时 将data初始为空 在main函数结束时进行序列化
}
return data;
}
//存储数据
public void store(HashMap<Integer, Express> data) throws IOException {
FileOutputStream fos = new FileOutputStream("cat.txt"); //这里的路径不能写其他项目序列化出来的文件!!!!!!!!!!!!!!
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(data);
oos.close();
fos.close();
System.out.println("数据存储成功");
}
}
4.客户端
package Chapter4_Homework05.client_self;
import Chapter4_Homework05.view.View;
import Chapter4_Homework05.bean.Express;
import Chapter4_Homework05.dao.ExpressDao;
import java.io.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.Scanner;
/**
* @program: 新职课作业代码
* @description: 客户端(self)
* @author: 张振亚
* @create: 2021-01-16 11:51
**/
public class Client_Self {
// 这样可以跨方法使用
private static Scanner input = new Scanner(System.in);
// 初始化视图对象
private static View v = new View();
// 初始化dao对象
private static ExpressDao dao = new ExpressDao();
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 传入本机地址和端口
Socket socket = new Socket("127.0.0.1", 65534);
// 向服务器发送请求
System.out.println("正在向服务器发送请求");
// 创建输入输出流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
ObjectInputStream ois = new ObjectInputStream(is);
boolean[][] cabinet = new boolean[10][]; // 二维数组表示快递柜位置是否被占用
HashMap<Integer, Express> data; // HashMap存放 <取件码,快递对象>
for (int i = 0; i < 10; i++) {
cabinet[i] = new boolean[10];
}
data = (HashMap<Integer, Express>) ois.readObject(); //接收由服务器传过来的数据
for (Express e : data.values()) {
cabinet[e.posX][e.posY] = true; // 表示此位置已被占用
}
//引入局部内部类 来解决socket对象无法在主函数外的静态方法内使用的问题!!!(没想到别的更好的办法)
class UClient {
void uClient(boolean[][] cabinet, HashMap<Integer, Express> data) throws IOException {
// Collection<Express> expresses = data.values();// 获得所有快递对象 便于遍历查找
// 1,获得取件码
int code = v.uMenu();
// 2,根据取件码取出快递
Express e = dao.findByCode(code, data.values());
if (e == null) {
v.printNull();
} else {
oos.writeObject(e);
v.success();
v.printExpress(e);
dao.delete(e, cabinet, data);
}
}
}
class GClient {
void gClient(boolean[][] cabinet, HashMap<Integer, Express> data) throws IOException {
while (true) {
int menu = v.gMenu();
//给服务器发送用户选择的功能号
os.write(menu);
switch (menu) {
case 0:
return;
case 1: { // 快递录入
// 1,提示输入快递信息
Express e = v.insert();
// 2,此快递是否已经存储过
Express e2 = dao.findByNumber(e.getNumber(), data.values());
// 3,存储快递
if (e2 == null) { // 未存储过
oos.writeObject(dao.add(e, cabinet, data)); //将添加的快递对象发送给服务器 //data.put(code, e); !!!
v.printCode(e);
} else { // 单号重复
v.expressExist();
}
break;
}
case 2: { // 快递修改
// 1,提示输入快递信息
String number = v.findByNumber();
// 2,查找数据
Express e = dao.findByNumber(number, data.values());
// 3,打印快递信息
if (e == null) {
v.printNull();
} else {
v.printExpress(e);
oos.writeObject(e);
// 4,提示修改
Express eNew = v.update(); // 只穿回来修改了公司和快递单号的类
Express eNewUpdate = dao.update(e, eNew, cabinet, data);// 这里只是为了强调 删除-修改的过程
oos.writeObject(eNewUpdate);
v.printExpress(e);
}
break;
}
case 3: { // 删除
// 1,输入快递单号
String number = v.findByNumber();
// 2,查找快递对象
Express e = dao.findByNumber(number, data.values());
if (e == null) {
v.printNull();
} else {
oos.writeObject(e);
v.printExpress(e);
int type = v.delete();
if (type == 1) {
dao.delete(e, cabinet, data); //data.remove(e.getCode()); 用键值删除 键值对
} else {
v.success();
}
}
break;
}
case 4: { // 查看所有
v.printAll(data.values()); // 在视图层显示
break;
}
}
}
}
}
m:
while (true) {
//输入功能序号
int menu = v.menu();
//给服务器发送用户选择的功能号
os.write(menu);
switch (menu) {
case 0:
break m;
case 1:
new GClient().gClient(cabinet, data);
break;
case 2:
new UClient().uClient(cabinet, data);
break;
}
}
//用户使用完毕,将存在客户端内存中的数据发送给服务器!!!
oos.writeObject(data);
}
}
5.服务器
package Chapter4_Homework05.server_self;
import Chapter4_Homework05.view.View;
import Chapter4_Homework05.bean.Express;
import Chapter4_Homework05.dao.ExpressDao;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Scanner;
/**
* @program: 新职课作业代码
* @description: 快递主函数
* @author: 张振亚
* @create: 2021-01-13 23:20
**/
public class Server_Self {
// 这样可以跨方法使用
private static Scanner input = new Scanner(System.in);
// 初始化视图对象
private static View v = new View();
// 初始化dao对象
private static ExpressDao dao = new ExpressDao();
public static void main(String[] args) throws IOException, ClassNotFoundException {
//启动服务器
ServerSocket serverSocket = new ServerSocket(65534);
//这里接收到客户端的socket,接下来用客户端的 socket对象 来使用方法,获取向客户端的输入流和输出流。
//只要是用socket调用的输入流或输出流。(eg.OutputStream os = socket.getOutputStream();)
//如果用socket获取的输入流,那么你所读入的数据来源就是你的客户端。
//就比如(eg.new FileOutputStream("SerializedData.txt");)你的读入的数据来源就是这个引号里的文件。
//同理,socket获取的输出流是你要写入文件的位置,即将数据写入(发送给)客户端。
//然后有了输入输出流的位置以后,将这些字节流经过各种过滤器,将这个原始的数据流包装成你需要的或者对方能接受的编码类型。再进行发送或写入。
// 等待连接
System.out.println("服务器端已启动\n等待客户端连接...");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("连接客户端成功");
System.out.println("等待客户端请求.....");
//引入多线程技术
new Thread(){
// 1,弹出身份选择菜单
@Override
public void run() {
try {
// 创建输入输出流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
ObjectInputStream ois = new ObjectInputStream(is);
boolean[][] cabinet = new boolean[10][]; // 二维数组表示快递柜位置是否被占用
HashMap<Integer, Express> data; // HashMap存放 <取件码,快递对象>
for (int i = 0; i < 10; i++) {
cabinet[i] = new boolean[10];
}
try {
data = dao.load(); //将文件中的数据反序列化加载到data对象中!!!
} catch (IOException e) {
data = new HashMap<>(); // 打开文件异常时 将data初始为空 在main函数结束时进行序列化
}
oos.writeObject(data); //将数据以序列化文件的格式发给客户端!!!
for (Express e : data.values()) {
cabinet[e.posX][e.posY] = true; // 表示此位置已被占用
}
//引入局部内部类 来解决socket对象无法在主函数外的静态方法内使用的问题!!!
class UClient {
void uClient(boolean[][] cabinet, HashMap<Integer, Express> data) {
try {
Express e = (Express)ois.readObject();
data.remove(e.getCode(), e);
}catch (ClassNotFoundException | IOException e){
System.out.println("取件失败!");
}
}
}
class GClient {
void gClient(boolean[][] cabinet, HashMap<Integer, Express> data) throws IOException {
while (true) {
//接收客户端发送过来的功能号
int menu = is.read();
switch (menu) {
case 0:
return;
case 1: { // 快递录入
try {
Express e = (Express)ois.readObject();
data.put(e.getCode(), e);
}catch (ClassNotFoundException e){
System.out.println("添加快递失败!");
}
break;
}
case 2: { // 快递修改
try {
Express eOld = (Express)ois.readObject();
Express eNew = (Express)ois.readObject();
data.remove(eOld.getCode());
data.put(eNew.getCode(), eNew);
}catch (ClassNotFoundException e){
System.out.println("修改快递失败!");
}
break;
}
case 3: { // 删除
try {
Express e = (Express)ois.readObject();
data.remove(e.getCode(), e);
}catch (ClassNotFoundException e){
System.out.println("删除失败!");
}
break;
}
case 4: { // 查看所有
break;
}
}
}
}
}
m:
while (true) {
//接收由客户端发送过来的功能号
int menu = is.read();
switch (menu) {
case 0:
break m;
case 1:
new GClient().gClient(cabinet, data);
break;
case 2:
new UClient().uClient(cabinet, data);
break;
}
}
/**
* 序列化由客户端返回回来的数据 HashMap<Integer, Express> data
*/
data = (HashMap<Integer, Express>) ois.readObject(); //接收 客户端使用结束后 传回来的客户端内存中的数据
dao.store(data);
// 在最后记得关闭连接
ois.close();
oos.close();
} catch (Exception e) {
}
}
}.start();
}
}
}