文章目录
图书管理小练习
前言: 本文 将 结合 类和对象, 封装 继承 ,多态, 抽象类, 以及 接口 来完成 一个图书 管理小练习 。
1.构建框架
我们 要明白一个道理 , 不管 干什么 都需要 一个大致的 框架 或 大致的 目标 ,如 人生一样, 不能 想到啥 做啥 ,而是 有一定的 规划 我们 才能 按照 这个规划 走下去,而不是 走到下 想到哪 。
正如 此 我们 想要完成 一个 图书 管理 小练习, 那么 我们 是不是 可以想到 书 , 书架 , 以及 我们的 管理 员 最后 还有我们的 用户 这几个 对象 ,
这里我们就 创建 了 两个 包 ,存放 在 BookExercise 这 包中 、
1.1Book 底下的 图书 类 和 书架类
下面 就 在 Book 包中 创建 我们 的 书 , 和 书架。
这里 我们 的 Book 是 书 里面的 属性 是 我们 书的 相关 属性 ,如 : 作者 , 书的 价钱 , 书 的 类型(如 : 小说 , 教材 等) , 这本书 是否 被 借出,
下面 就来 创建我们的 书 : 为了 体现我们的 封装 , 我们的 字段 都使用 private 修饰 ,提供 特有 的 get 和 set 方法, 这里 就可以 通过我们的 构造 方法 来 为这些 字段 赋值 , 但 是否借出 ,可以 不通过 构造 方法 赋值 , 默认 这本 书 是 没有被 借出的。
注意: 别 想 憨憨 一样 自己 手敲 ,之前 介绍 过 编译器 通过 快捷 键生成 set 和 get 方法 , 以及 构造 方法 ,最后 还可以 重写 我们 的 toString 方法(打印我们 书 的 信息 )。
Book 类
public class Book {
private String name; // 书名
private String author; // 作者
private int price; // 价格
private String type; // 类型
private boolean isBorrowed;// 是否借出
// 这个 可以不用 写进 toString, new 一个 isBorrowed 默认没有借出
给定 普通成员变量。 封装起来 给定 特有 的get 和 set 方法
// 构造方法
public Book(String name, String author, int price, String type) {
this.name = name;
this.author = author;
this.price = price;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isBorrowed() {
return isBorrowed;
}
public void setBorrowed(boolean borrowed) {
isBorrowed = borrowed;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", type='" + type + '\'' +
", isBorrowed=" + isBorrowed +
'}';
}
}
最后 你会 发现 你就 随便 一点 , 就 完成了 50 - 60 行 代码 。下面继续
图书 的 基本 信息 有了 ,那么我们 就需要 创建我们 存放 图书 的 书架 了 。
第一步 :创建 一个 BookList 类 , 当作我们的 书架 (这里我们 采用 顺序表 )
第二步 : 创建 一个 对象 数组 (里面 是 对象 的 地址) , 用来 存放 我们 的 图书 ,
同样为了 体现 封装 性 我们 通过 关键字 private
将 这个 数组 封装起来 后面 通过 我们的 get 和 set 方法 来 获取 和 设置 这个 数组 即可 。
有了 数组 , 我们 是不是 要想 , 如果 计算 这个 数组 中 存放 的 图书 数量 呀 , 这里 我们就可以 创建 一个 size
变量 ,添加 一本书 ,就 让 size++
, 删 出 一本书 就 让 size--
,同样 我们 也将这个 变量被 private
修饰 。
这里 就 完成 我们 书架 的 一大半了, 为了 好看 我们现在 书架 中 存放 3 本书 (既然 是 图书 馆 ,咋可能没有 书 呢?), 这里 我们 有两种 方法 存放 这 三本书 , 一 通过 方法 , 二 通过 构造 方法 。
方法 一: 通过 方法
方法 二 : 通过 构造 方法 (注意 size 需要 赋值 ,这里 就说明 书架中 已有 3 本书 )。
想一想 这两种 方法 哪一种 好 呢 ?
我觉得 第二种 好点 , 你想 ,我们 创建 对象时 , 就是 通过 构造方法 来 创建, 这时 构造方法, 就 将 三本书 放进 了 书架 , 而 第一种 ,我们 创建 对象 后,还需要 对象 取调用 func方法 , 多了 一步, 所以 第二种 好点 。
附上代码: BookList 类
public class BookList {
// 书架 存放 书的 类 。
private Book[] books = new Book[10];
private int size = 0;
// 通过 getPos 获取当前 pos 位置的 书
public Book getPos(int pos) {
return books[pos];
}
// 设置 pos 位置 放一本书
public void setBook(int pos,Book book) {
this.books[pos] = book;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
// public void func(){
// books[0] = new Book("三国演义","罗贯中",17,"小说");
// books[1] = new Book("西游记","吴承恩",47,"小说");
// books[2] = new Book("水浒传","施耐庵",37,"小说");
//
// this.size = 3;
// }
public BookList() {
books[0] = new Book("三国演义","罗贯中",17,"小说");
books[1] = new Book("西游记","吴承恩",47,"小说");
books[2] = new Book("水浒传","施耐庵",37,"小说");
this.size = 3;
}
}
书 和 书架 我们 完成了 , 那么 就来 看看我们的 User包底下 的 管理员 和 用户 是如何写的 。
1.2 User 底下的 管理员类 和 用户类
场景构造 : 想一想我们 的 管理员 和 用户 ,分别 在 图书 馆 中 有 的 权限 ,
管理员: 添加图书 , 删除 图书, 查找 图书 ,显示 全部图书。 最后 还有 一个 退出系统(用来 退出 整个 程序 )
用户: 查找 图书 , 借阅 图书 , 归还 图书 ,退出系统。
此时 我们 就 将 我们 管理员 和 用户 的 所有 权限 给 列举出来 (这些 权限 也就是 我们 后面需要实现的 功能) ,还有 其他的功能 可以 自行 补充 ,
这里我们 就来 实现 一下我们 的 管理员 类 , 和 用户 类 。
这 里 我们 需要 创建 三个 类 , 一个 管理员 , 一个 普通 用户 ,还有 一个 共同的 父类 存放 我们 的字段 如: 姓名 。
另外 通过 这 个 父类 我们 还可以 实现 (向上转化 , 动态绑定 ,多态)等 操作。
这里 我们 就需要 一个 目录 将上面这些 功能 全部 罗列 出来 让 管理员 或 用户 进行 选着 。
这里我们 就 将 父类 User 中的 menu 写成我们的 抽象方法 , 那么 User 就 是我们的 抽象类 (这 就 将我们学习的 抽象类 给 用到了)
有了 普通 用户 , 和 管理员 ,那么我们 需要 如何调用 呢 ?
下面 就来 演示 一下:
下面 就 根据 选着 来 调用 相应 的 类 , (1. 管理员 or 0 . 普通用户)
这里 就 可以 得到 我们 需要的 对象。 那么 这里 我们 需要那 什么作为返回值 呢 ?
想一想 ,提示向上转型 。
这里 我们 能否想到 之前 讲到 过的 向上 转型 , 这是 第三种 方法 ,通过 返回值 实现 向上转型 ,完成 了 我们的 父类 引用 引用 了 子类对象 。
这里 就完成了 我们 对 这两个类 的 引用 。
这里我们 只 实现 了 我们 的 目录 , 下面就来 实现我们的 方法 。
1.3 管理员 and 用户 对应 方法串联
这些 方法 我们 可以 在 BookList 中 实现 , 但 我们 毕竟 是 要将 之前 学习 的 知识 串起来, 那么我们 就通过 接口, 和 类 来 实现 我们的 方法,
这里 就创建 第三个 包 :operation
用来 存放 我们 实现 的 方法(如 : 新增 图书 , 删除 图书 等 )。
这里 我们 就 将 全部 方法 的 类创建 出来了 ,可以 看到 还有 一个 接口 , 这个 接口 非常 关键 后面 你就能 明白了 。
补: Add: 增加 图书 , Boorow : 借阅图书 , DelOperation : 删除 图书 , DisplayOperation : 打印图书 或 显示 图书
ExitOperation : 退出系统 FindOpeation : 查找 图书 ReturnOperation : 归还 图书 。
有了上面 的 方法 我们就来 实现 一下 。
但有 一点我们需要 注意 , 这里 我们 需要 让每个 类实现我们的 接口Ioperation
然后 实现接口 中 的 抽象 方法 work 在重写的 work 中 实现我们的 方法 逻辑 , 如添加元素,删除 元素 。 为啥 呢 ?
这个 我们想 通过接口 是不是 可以完成 向上 转向 ,然后 通过 一个 work 方法 调用 这些 类 的work 方法 (这里 就 发生 了 动态绑定 和 多态)。 就 不需要 左一个方法 ,右一个 方法 ,方便我们的 调用 。
这里 我们 先 完成 这些 方法 , 方法 的 实现 的 逻辑下面在完成 ,先 将 这些 东西 串起来。
此时 我们 就来 想 如何 才能 调用 到 相对 的 方法 ,如 : 管理员 调用 管理员 所用有的 权限 ,普通用户 调用 所有 的权限 。
我们 知道 对象 数组, 数组 中存放 的是 我们 对象的 地址,通过 下标 来访问 对应 的 对象 ,那么 能不能 让 接口 成为 一个 数组 ,然后 存放 管理员 ,和 普通用户相对 的 方法 呢?
这里就来 尝试 一下 。
注意:我们 管理员 类 和 普通 用户类 都拥有 这个 对象 数组, 这就算 一个共性 ,我们就可以定义到 父类 User 中 ,然后 在考虑其他 。
这个数组 我们 就定义 好 了, 接下来 就需要考虑, 这个数组 的 內部 所含的 对象 了 。
如:
管理员: 添加图书 , 删除 图书, 查找 图书 ,显示 全部图书。 最后 还有 一个 退出系统(用来 退出 整个 程序 )
这里 , 添加图书算一个 对象, 那么 我们就通过 new 类名创建 一个对象放入 这个 数组 当中,其他类似。
这样是不是 就通过 我们的 构造方法, 将 这些 方法全部 存放到我们的 ioperation 数组当中 , 这里 我们 的方法 需要 对着我们目录提供的 顺序 哦。
注意:我们一次 只能 选着 管理员 或 普通成员 ,所以 这个 ioperation 数组 只会 初始化一次。
那么同理 用户 也是 这样的
用户: 查找 图书 , 借阅 图书 , 归还 图书 ,退出系统。
下面 就来使用 一下,
可以看到我们就成功 的调用了 方法,但还是 有很大的 问题 。
第一: 我们 的目录 只能看 ,难以 选择选择相对 的 方法 。
那么 我们 能不能 优化 现在的 代码呢?
其实 非常简单,我们 只需要在目录 方法 里 面 添加 输入 语句 即可。
下面 就来 使用 一下 :
这里还有一些东西没有完成 ,下面我们 先来实现 一下我们 的 方法 。 在回头来看一下 。
2.管理员 and 用户 方法 实现
注意 : 我们 之前 的work 方法 中 没有写 参数 , 并非是 不要参数, 你想一想, 我们 是不是需要 对书架进行 操作,添加 图书 ,删除 图书 等 , 那么是不是 需要 添加 一个 BookList 类型的参数 呢 ???
2.1Add - 添加 图书
在 添加 图书 的需要 注意 一个 小细节 : 如果 我们 先 输入 数字 在输入 字符串 这里 就会出现 问题,(这个问题 在逻辑 控制那篇文章 特意 讲过)
如: 这里我们输入 一个 20 就会直接 退出程序 而我们的 str 什么 都没 赋值,
此时 我们 通过 调式能知道 这里 str会拿到上一行 输入 数字的换行符 空 , 所以我们 什么输出不了 ,也无法 赋值, 这里 的 解决方式就可 在 写一个 sc.nextLine()将 这个 换行符 吸收 。
附上代码:
public class Add implements Ioperation {
@Override
public void work(BookList bookList) {
// 添加 图书 。
System.out.println("新增 图书 ");
System.out.println("新增 图书 的 名字");
String name = sc.nextLine();
System.out.println("新增 图书的 作者");
String author = sc.nextLine();
System.out.println("新增 图书的 价格");
double price = sc.nextDouble();
// 吸收 赋值price 的 换行符
sc.nextLine();
System.out.println("新增 图书的 类型");
String type = sc.nextLine();
Book book = new Book(name, author, price, type);
// 判断是否 需要 扩容。
bookList.func();
// 拿到 当前 size 的 大小。
int size = bookList.getSize();
// 因为我们 的数组 封装起来了 ,提供 了 特有的 方法 让他添加 图书 ,这里就 通过这个方法
//完成我们的 图书 的添加
bookList.setBooks(size, book);
// 此操作为更新 图书
bookList.setSize(size + 1);
System.out.println("新增图书成功");
}
}
2.2 Borrow - 借阅图书
我们 的 借阅图书 , 非常简单 只需要 , for循环 在 书架 中 找 通过 equals 对象 比较 如果 相同 返回 true , 找到 了 我们 就 将 isBorrowed 设为 true 即可 true表示 这本书 已被借出
public class Borrow implements Ioperation {
@Override
public void work(BookList bookList) {
// 借阅 图书 。
// 1.判断 该图书是否 存在 ,或 是否 被 借出 。
System.out.println("借阅 图书 区 ");
System.out.println("输入 想要 借阅 图书 的 名字");
String name = sc.nextLine();
int size = bookList.getSize();
for (int i = 0; i < size; i++) {
// 通过 我们 设计 方法 拿到 当前 i 位置 的 图书 ,
Book book = bookList.getBooks(i);
if (book.getName().equals(name)) {
// 此时 说明找到了 我们需要的 书 。
if (book.getisBorrowed()) {
System.out.println("该图书 已被借出 ");
} else {
book.setBorrowed(true);
System.out.println("借阅成功");
System.out.println(book);
}
}
}
System.out.println("本 图书 馆没有 此 书");
}
}
2.3 DelOperation - 删除 图书
那么 删除 我们的 图书 不也一样吗 ,现在 数组中 找到 要删除 图书的 下标 ,然后 不就 更 上面 一样 了 ,直接 覆盖 就好 。
public class DelOperation implements Ioperation {
@Override
public void work(BookList bookList) {
// 删除图书。
System.out.println("删除 图书 ");
System.out.println("输入 要删除 的 图书 名字");
String name = sc.nextLine();
// 顺序表 删除 挪位子。
int index = 0;
int size = bookList.getSize();
int i = 0;
for (i = 0; i < size; i++) {
// 通过 getBooks 拿到我们的 i 位置的 下标
Book book = bookList.getBooks(i);
if (book.getName().equals(name)) {
// 这里 不考虑 重复 的 书 ,只删除 第一本。
index = i;
//拿到 下标我们就 直接 退出 循环 即可
break;
}
}
// 如果 i 大于我们 的 图书 的 数量 ,那么 就说明 没有找到我们的 图书
if (i >= size) {
System.out.println("没有你要删除的 图书");
return;
}
for (int j = index; j < size - 1; j++) {
// 拿到 j + 1 本 书
Book book = bookList.getBooks(j + 1);
bookList.setBooks(j, book);
}
// 将 最后 一本 书 置为 空, 要不 会 出现 内存 泄露
bookList.setBooks(size - 1, null);
bookList.setSize(size - 1);
System.out.println("删除 成功");
}
}
2.4 DisplayOperation - 显示 图书
public class DisplayOperation implements Ioperation {
public void work(BookList bookList) {
// 打印 图书 / 显示 图书
System.out.println("显示 全部 图书");
int size = bookList.getSize();
for (int i = 0; i < size; i++) {
Book book = bookList.getBooks(i);
System.out.println(book);
}
}
}
2.5 ExitOperation - 退出 系统
public class ExitOperation implements Ioperation{
@Override
public void work(BookList bookList) {
System.out.println("退出 程序");
// 这里 exit(0) 表示正常 退出 系统
System.exit(0);
}
}
2.6 FindOperation - 查找图书
public class FindOperation implements Ioperation {
@Override
public void work(BookList bookList) {
// 查找 图书
System.out.println("查找 图书 区 ");
System.out.println("输入 要查找 的 图书 ");
String name = sc.nextLine();
int size = bookList.getSize();
for (int i = 0; i < size; i++) {
Book book = bookList.getBooks(i);
if(book.getName().equals(name)){
System.out.println("找到此书,信息如下");
System.out.println(book);
return;
}
}
System.out.println("未 查找到 此 书");
}
}
2.7 ReturnOperation - 归还图书
public class ReturnOperation implements Ioperation{
@Override
public void work(BookList bookList) {
// 归还 图书 ,
System.out.println("归还 图书 区");
System.out.println("请输入你要归还图书的名字");
String name = sc.nextLine();
int size = bookList.getSize();
for(int i = 0;i<size;i++){
Book book = bookList.getBooks(i);
if(book.getName().equals(name)){
// 将原本 借出 图书 的 true 置为 false
book.setBorrowed(false);
System.out.println("归还成功 !!!");
System.out.println(book);
return;
}
}
System.out.println("此 书 不是 本 图书 馆 ,归还 失败");
}
}
上面 我们 的功能 就完成了,这里我们只是 简单的完成 ,还有很多情况 没有考虑, 如: 删除的 书 有很多本此时就 只能删除一本, 如扩容 等
最后 我们 就来 完成最后 的 步骤 。
一开始 我们 是 这样 完成我们 的 方法调用 ,这 里 我们 就可以 简化 成 一个 User 类 中 的 一个 方法 即可 , 方便 还 易懂。
这样就完成了 我们 的 图书 管理小练习。
最后我们 将我们 的 操作 换成 循环 就能 使用多个方法 。
但 最后我们 还有 一个 细节 每 弄 就是 图书 是否被 借出 , 这里 就可以 使用 三目运算符 ,来判断 如果 isBorrowerd 为 true 就说明 被 借出 , 为false 就 说明 没被 借出 。
这里 图书 管理 小练习 就 完成了 。
下文预告: String类的 常见 方法使用、