热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

任务卡_03Java核心类库_第6节网络编程

一,快递管理训练任务1,题目描述还记得之前的快递管理吗?我们将数据存储在文件中,其实数据存储在客户端中是很不安全的,今天我们来学习网络编程,客户端后续只用来收集用户的操作,需要存储

目录

一,快递管理训练任务


1,题目描述

2,源代码

2.1 bean.Express

2.2 dao.ExpressDao

2.3 main

2.4 view.View

3,总结

3.1 核心数据结构改动

3.2 ExpressDao

3.3 Main与ServerMain、ClientMain

3.4 说明

3.5 遇到的问题

3.6 思考

二,图书管理训练任务(选做)

1,题目描述



一,快递管理训练任务

1,题目描述


还记得之前的快递管理吗?

我们将数据存储在文件中,其实数据存储在客 户端中是很不安全的,今天我们来学习网络编程,客户端后续只用来收集用户 的操作,需要存储的数据都存储在服务器中。 

为了保证服务器能同时连接多个客户端,记得在服务器引入多线程技术。 接下来加油学习吧!


2,源代码

代码结构

2.1 bean.Express

package bean;
import java.io.Serializable;
import java.util.Objects;
/**
*
*/
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.2 dao.ExpressDao

package dao;
import bean.Express;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
// 实现可序列化标记接口 使得dao对象支持序列化与反序列化
public class ExpressDao {
private File file = new File("SerializedData.txt");
private boolean[][] cabinet = new boolean[10][]; // 二维数组表示快递柜位置是否被占用 true已占用 false未占用
private ArrayList expresses = new ArrayList<>(); // 存放所有的Express对象 便于遍历
private Random random = new Random(); // 用于生成随机数
/**
* 反序列化获得快递柜中存放的对象HashMap data
*/
public void readFromFile() {
try (FileInputStream fis = new FileInputStream(file)) {
ObjectInputStream ois = new ObjectInputStream(fis);
expresses = (ArrayList) ois.readObject(); // 反序列化读取对象
ois.close(); // 关闭输入流
} catch (IOException | ClassNotFoundException e) {
expresses = new ArrayList(); // 打开文件异常时 将expresses初始为空
}
}
/**
* 序列化存储对象HashMap data
* @throws IOException
*/
public void writeToFile() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(expresses); // 序列化对象
oos.close();
}
/**
* 初始化数据结构:cabinet(表示快递柜是否被占用),expresses(存储)
*/
public void init() {
for(int i = 0; i <10; i++){
cabinet[i] = new boolean[10];
}
for(Express e : expresses) {
cabinet[e.posX][e.posY] = true; // 表示此位置已被占用
}
}
/**
*
* @param e 新加入的快递对象
* @return
*/
public synchronized boolean add(Express e){
int size = expresses.size();
if(size >= 100){
return false;
}
// 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(); // 获得没有重复的取件码
e.setCode(code);
e.posX = x; // 快递柜存放快递的位置
e.posY = y;
size++; // 快递数目加一
cabinet[x][y] = true; // 此位置已被占用
expresses.add(e); //
return true;
}
/**
* 遍历所有对象 生成独一无二的取件码
* @return
*/
private int randomCode(){
while (true) {
int code = random.nextInt(900000) + 100000; // 范围(000000-899999)+1000000
Express e = findByCode(code);
if(e == null) { // 说明取件码未重复
return code;
}
}
}
/**
* 快递员根据快递单号查询HashMap中存放的快递
* @param number
* @return
*/
public Express findByNumber(String number){
for(Express e : expresses) {
if(e.getNumber().equals(number)) {
return e;
}
}
return null;
}
/**
* 根据取件码查询快递
* @param code 取件码
* @return 查询到结果 查询失败返回null
*/
public Express findByCode(int code){
for(Express e : expresses) {
if(e.getCode() == code) {
return e;
}
}
return null;
}
/**
* 多余的操作 为了MVC更圆润
* @param oldExpress
* @param newExpress
*/
public synchronized Boolean update(Express oldExpress, Express newExpress){
delete(oldExpress);
return add(newExpress);
}
/**
* 删除特定的快递对象
* @param e
*/
public synchronized boolean delete(Express e){
cabinet[e.posX][e.posY] = false;
return expresses.remove(e);// 删除指定对象
}
/**
* 获取所有的快递对象
* @return
*/
public synchronized ArrayList getAll() {
return expresses;
}
}

2.3 main

1)main.ClientMain

package main;
import bean.Express;
import view.View;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ClientMain {
private Socket socket;
private View v = new View();
public static void main(String[] args) throws IOException {
ClientMain client = new ClientMain();
client.link();
}
/**
* 创建套接字,与服务端进行连接;
* 创建对象输入/输出流,与服务端进行数据交互;
* @throws IOException
*/
public void link() throws IOException {
OutputStream os = null;
InputStream is = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
socket = new Socket("127.0.0.1", 8888);
v.connectSuccess();
is = socket.getInputStream();
os = socket.getOutputStream();
oos = new ObjectOutputStream(os);// 由于服务器是先ois后oos 为了保证配对 这里需要顺序调换
ois = new ObjectInputStream(is);
o:while (true) {
int num = v.menu();// 获得角色选择码
oos.writeInt(num);
oos.flush();
switch (num) {
case 0:
break o;
case 1:
gClient(oos, ois);
break;
case 2:
uClient(oos, ois);
break;
default:
v.choiceError();
break;
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null) ois.close();
if(oos != null) oos.close();
if(socket != null) socket.close();
}
}
/**
* 客户端管理员模块
* @param oos
* @param ois
* @throws IOException
* @throws ClassNotFoundException
*/
public void gClient(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
while (true) {
int num = v.gMenu();// 获得用户输入的功能码
oos.writeInt(num);// 向服务器传送功能码 保证进入同样的功能模块
oos.flush();
switch (num) {
case 0:// 退出
return;
case 1:// 插入
insert(oos, ois);
break;
case 2:// 修改
update(oos, ois);
break;
case 3:// 删除
delete(oos, ois);
break;
case 4:// 显示所有
printAll(oos, ois);
break;
default:
v.choiceError();
break;
}
}
}
/**
* 客户端用户模块
* @param oos
* @param ois
* @throws IOException
* @throws ClassNotFoundException
*/
public void uClient(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
while (true) {
int num = v.uMenu();
oos.writeInt(num);
oos.flush();
switch (num) {
case 0:
return;
case 1:
int code = v.getExpress();
oos.writeInt(code);
oos.flush();
Express e = (Express) ois.readObject();
if(e != null) {// 查询到有快递存在
v.printExpress(e);
if(ois.readBoolean()) v.success();
else v.fail();
} else {
v.printNull();// 取件码对应快递不存在
}
break;
default:
v.choiceError();
break;
}
}
}
/**
* 插入快递对象;
* 利用view对象获取将要插入快递对象,并将其传送给服务端
* @param oos
* @param ois
* @throws IOException
* @throws ClassNotFoundException
*/
public void insert(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
Express e = v.insert();
oos.writeObject(e);
oos.flush();
Express e1 = (Express) ois.readObject();// 返回对象为空 表示当前快递单号尚未被使用
if(e1 == null) {
if(ois.readBoolean()) {// 插入成功
v.success();
} else {
v.fail();
}
} else {
v.expressExist();
}
}
/**
* 删除快递对象
* @param oos
* @param ois
* @throws IOException
* @throws ClassNotFoundException
*/
public void delete(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
String id = v.findByNumber();
oos.writeObject(id);
oos.flush();
Express e = (Express) ois.readObject();
if(e != null) {
int num = v.delete();// 再次向用户确认是否删除
oos.writeInt(num);
oos.flush();
switch (num) {
case 1:// 确认删除
if(ois.readBoolean()) v.success();
else v.fail();
break;
default:// 取消删除或退出
break;
}
} else {
v.printNull();
}
}
/**
* 更新快递对象
* @param oos
* @param ois
* @throws IOException
* @throws ClassNotFoundException
*/
public void update(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
String id = v.findByNumber();
oos.writeObject(id);
oos.flush();
Express e = (Express) ois.readObject();
if(e != null) {// 被更新的快递对象存在
Express e1 = v.update();
oos.writeObject(e1);
oos.flush();
if(ois.readBoolean()) v.success();
else v.fail();
} else {
v.printNull();
}
}
/**
* 打印快递对象
* @param oos
* @param ois
* @throws IOException
* @throws ClassNotFoundException
*/
public void printAll(ObjectOutputStream oos, ObjectInputStream ois) throws IOException, ClassNotFoundException {
// ArrayList expresses = (ArrayList) ois.readObject();
// v.printAll(expresses);
Express[] es = (Express[]) ois.readObject();
List expresses = Arrays.asList(es);
v.printAll(expresses);
}
}

2)main.ServerMain

package main;
import bean.Express;
import dao.ExpressDao;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class ServerMain {
private ServerSocket serverSocket;
private ExpressDao dao = new ExpressDao();
private int numOfClient = 0;
// 服务器
public static void main(String[] args) throws IOException {
ServerMain server = new ServerMain();
server.start();
}
/**
* 启动服务器,并与客户端进行连接
*/
public void start() {
try {
serverSocket = new ServerSocket(8888);
System.out.println("服务器已启动");
dao.readFromFile();// 从文件中读取数据
dao.init();// 初始化数据结构
System.out.println("数据初始化成功");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("第" + (++numOfClient) + "个客户端连接了");
new Thread() {
@Override
public void run() {
try {
receive(socket);// 准备连接 进入主功能模块
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(serverSocket != null){
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 创建对象输入/输出流,与客户端进行数据交互;
* 进入主模块,选择角色;
* @param socket
* @throws IOException
*/
public void receive(Socket socket) throws IOException {
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(is);
ObjectOutputStream oos = new ObjectOutputStream(os);
try(is; os; ois; oos) {// 这种方式可以在try/catch执行结束后 自动关闭资源
o: while (true) {
switch (ois.readInt()) {
case 0:// 退出
dao.writeToFile();// 退出服务器端 将数据对象写回文件
break o;
case 1:
gClient(ois, oos);// 进入管理员操作功能模块
break ;
case 2:
uClient(ois, oos);// 进入用户操作功能模块
break ;
default: break ;
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 服务端——管理员模块,负责与客户端——管理员模块进行数据交互
* @param ois
* @param oos
* @throws IOException
* @throws ClassNotFoundException
*/
public void gClient(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
while (true) {
switch (ois.readInt()) {
case 0:// 退出
return;
case 1:// 插入
insert(ois, oos);
break;
case 2:// 修改
update(ois, oos);
break;
case 3:// 删除
delete(ois, oos);
break;
case 4:// 显示所有
printAll(ois, oos);
break;
}
}
}
/**
* 服务端——用户模块,负责与客户端——用户模块进行数据交互
* @param ois
* @param oos
* @throws IOException
*/
public void uClient(ObjectInputStream ois, ObjectOutputStream oos) throws IOException {
while (true) {
switch (ois.readInt()) {
case 0:
return;
case 1:// 取件
Express e = dao.findByCode(ois.readInt());// 根据客户端传过来的取件码 查找快递对象
oos.writeObject(e);// 向客户端传送查找的对象
oos.flush();
if(e != null) {
oos.writeBoolean(dao.delete(e));
oos.flush();
}
break;
default: break;
}
}
}
/**
* 插入快递对象;
* 接受客户端发来的新快递对象,用dao对象对数据进行操作,并将操作结果返还给客户端
* @param ois
* @param oos
* @throws IOException
* @throws ClassNotFoundException
*/
public void insert(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
Express e = (Express) ois.readObject();
Express e1 = dao.findByNumber(e.getNumber());// 根据快递单号判断对应快递是否已存在
oos.writeObject(e1);
oos.flush();
if(e1 == null) {
oos.writeBoolean(dao.add(e));
oos.flush();
}
}
/**
* 删除快递对象
* @param ois
* @param oos
* @throws IOException
* @throws ClassNotFoundException
*/
public void delete(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
String id = (String) ois.readObject();
Express e = dao.findByNumber(id);
oos.writeObject(e);
oos.flush();
if(e != null) {// 快递对象存在
switch (ois.readInt()) {// 再次向用户确认是否删除
case 1:
oos.writeBoolean(dao.delete(e));
oos.flush();
break;
default:
break;
}
}
}
/**
* 更新快递对象
* @param ois
* @param oos
* @throws IOException
* @throws ClassNotFoundException
*/
public void update(ObjectInputStream ois, ObjectOutputStream oos) throws IOException, ClassNotFoundException {
String id = (String) ois.readObject();
Express e = dao.findByNumber(id);
oos.writeObject(e);
oos.flush();
if(e != null) {
Express e1 = (Express) ois.readObject();
oos.writeBoolean(dao.update(e, e1));
oos.flush();
}
}
/**
* 向客户端传送所有快递对象
* @param ois
* @param oos
* @throws IOException
*/
public void printAll(ObjectInputStream ois, ObjectOutputStream oos) throws IOException {
// oos.writeObject(dao.getAll());
// oos.flush();
ArrayList list = dao.getAll();
Express[] expresses = new Express[list.size()];
list.toArray(expresses);
oos.writeObject(expresses);
oos.flush();
}
}

2.4 view.View

package view;
import bean.Express;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Scanner;
/**
* 视图层
* 只负责展示视图 不包含其他任何逻辑
*/
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修改快递信息
*/
public Express update(){
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;
}
/**
* 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(List es){
int count = 0;
for(Express e : es) {
count++;
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.posX + 1) + "排," + (e.posY + 1) + "列; " +
"快递公司:" + e.getCompany() + "; " + "快递单号:" + e.getNumber() + ";" +
"取件码:" + e.getCode() + ";");
}
/*
-----------------------------------------------------------------
*/
/**
* 获得用户输入的取件码(这里简化,只要取件码相同,就算取件成功)
* @return 用户输入的合法功能码(6位)
*/
public int uMenu(){
System.out.println("根据提示输入功能序号:");
System.out.println("0,退出");
System.out.println("1,取出快递");
String s = input.nextLine();
int funcNum = -1;
try{
funcNum = Integer.parseInt(s);
}catch (NumberFormatException e){ // 格式异常 递归继续获取功能码
System.out.println("格式异常");
return uMenu();
}
if(funcNum <0 || funcNum > 1){ // 功能码不合法
System.out.println("功能码不合法");
return uMenu();
}
return funcNum;
}
/**
* 用户取件
* @return
*/
public int getExpress() {
System.out.println("根据提示进行取件:");
System.out.print("请输入取件码:");
String s = input.nextLine();
int code = -1;
try{
code = Integer.parseInt(s);
}catch (NumberFormatException e){ // 格式异常 递归继续获取功能码
System.out.println("格式异常");
return getExpress();
}
if(code <100000 || code > 999999){ // 功能码不合法
System.out.println("输入有误,请重试!");
return getExpress();
}
return code;
}
public void expressExist(){
System.out.println("此快递单号已存在,请勿重复存储");
}
public void printCode(Express e) {
System.out.println("新快递的取件码为:" + e.getCode());
}
public void success(){
System.out.println("操作成功!");
}
public void fail(){
System.out.println("操作失败!");
}
public void choiceError() {
System.out.println("输入选项有误,请重新输入!");
}
public void printNull(){
System.out.println("快递不存在,请检查输入");
}
public void connectSuccess() {
System.out.println("服务器连接成功");
}
}

3,总结

与上一次的任务卡@&再见萤火虫&【任务卡_03-Java核心类库_第4节 IO】相比,有着较大的改动。

3.1 核心数据结构改动


将HashMap data删除,只保留Collection expresses;


  • 虽然可以根据HashMap快速的查找相应的Express对象,但是会导致快递对象的重复存储,也就是说在data中和expresses中存储了许多重复的快递对象,所以此次修改,只保留了expresses数据结构;

数据存储由main函数转移至dao对象中;


  • 将数据存储在main中,利用dao对象对数据进行操作需要传送大量的参数,使得函数看起来较为冗杂;

  • main函数中声明大量数据结构,使得主函数看起来较为复杂,破坏了整体的结构清晰感;



3.2 ExpressDao


1)将所有快递对象数据存放在dao的一个属性中(上一次IO任务卡,把他们放在了main方法中了);

2)取消根据取件码查找快递对象的HashMap数据结构,改成用ArrayList存储。避免对象重复存储占用大量空间;

3)插入、删除、更新、查询所有等方法使用synchronized进行修饰,保证线程安全;


3.3 Main与ServerMain、ClientMain


1)将Main拆解成ServerMain和ClientMain两个方法,一个负责服务端,一个负责客户端;

2)客户端和服务端需要保证大致相同的逻辑结构,即当客户端由状态1转变为状态2(方法功能,接受数据对象及类型等发生改变),服务端需要侦听这种状态转变,并进行同样的状态转移操作。反之同理;

3)客户端与服务端的数据交换是一去一回,严格配对!即便是在new输入输出流时也要保持一致!

4)服务器启动专门声明一个start方法完成,主要作用是:创建ServerSocket、反序列化读取对象信息、初始化数据结构、通过while循环实现多线程、线程run方法只用来调用receive方法(进入角色选择模块);

5)服务端的receive和客户端的link 是互相配对的。在这两个方法中创建ois(ObjectInoutStream)、oos(ObjectOutputStream),并将它们作为参数,进行数据交互;


3.4 说明


实现了多线程、集合、IO等技术;

较好的进行方法设计,使得项目整体结构清晰,冗余度较低;

基本完成题目要求功能:序列化反序列化、多线程、集合等;


3.5 遇到的问题

1)客户端执行到获取ObjectInputStream时卡住了,不再向下执行

咨询老师之后,发现问题。服务器与客户端在数据传输时,不但需要在交互阶段需要配对,声明赋值时也要配对,即输出对应输入、输入对应输出!

2)在dao中对数据进行操作后,快递对象集合确实发生变化了(在服务端打印出来看过)。但是在客户端进行printAll时,第一次是对的,后面不管怎么修改,再次printAll结果都不会改变

注释掉的,是原先有问题的代码。请教老师给出的解答是,流用完之后没有关闭,而且是作为参数进行传递,所以借助于流在客户端和服务器之间传递的集合对象expresses没有发生改变,依旧是第一次传输的数据,因而结果不发生改变。

但是有两点疑问:(以后再回头看看吧)


  1. flush的作用不就是清楚缓冲吗?为什么不起作用?

  2. 为什么把ArrayList对象转换为对象数组进行传输,就可以解决问题?是因为每次传输时,服务器都重新new了数组吗?



3.6 思考

1)try/catch与直接抛出异常相比有什么优缺点?

2)将流作为参数进行传递,不管对原数据进行什么操作,多次调用方法,获取流中传输的数据依旧是最开始传输的那一次?那flush的意以何在?

3)服务端、客户端进行数据交互时,不仅在传输数据需要一去一回,创建ois(ObjectInoutStream)、oos(ObjectOutputStream)时也要遵循这样的规则

二,图书管理训练任务(选做)

时间原因,有空再补o( ̄┰ ̄*)ゞ

1,题目描述


还记得之前的图书管理吗?我们将数据存储在文件中,其实数据存储在客 户端中是很不安全的,今天我们来学习网络编程,客户端后续只用来收集用户 的操作,需要存储的数据都存储在服务器中。 

为了保证服务器能同时连接多个客户端,记得在服务器引入多线程技术。 接下来加油学习吧! 

1. 管理员登陆 

2. 图书管理 

2.1 图书新增 

2.2 图书修改 

2.3 图书删除 

2.4 根据图书名称模糊查找图书 

2.5 查看所有图书 (三种排序) 

——价格从高到低排序 

——价格从低到高排序 

——新旧排序(出版日期排序)




章节汇总在这里(づ ̄3 ̄)づ╭❤~@&再见萤火虫&【03-Java核心类库】


对学习Java感兴趣的同学欢迎加入QQ学习交流群:1126298731

有问题欢迎提问,大家一起在学习Java的路上打怪升级!(o゜▽゜)o☆[BINGO!]



推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
author-avatar
asdasdasd
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有