📢博客主页:🏀九九舅舅酒酒🏀
📢欢迎点赞 👍 收藏 ⭐留言 📝 欢迎讨论!👏
📢本文由 【九九舅舅酒酒】 原创,首发于 CSDN🙉🙉🙉
📢由于博主是在学小白一枚,难免会有错误,有任何问题欢迎评论区留言指出,感激不尽!✨个人主页
📖精品专栏(不定时更新)【JavaSE】 【MySQL】【LeetCode】【Web】【操作系统】
一、前言
本篇文章只使用纯JavaSE基础语法。附带精解注释。
通过这个小项目实现本人对Java前期学习内容(包括继承多态,抽象类和接口 及其之前的内容)的复习和总结。
由于本人水平有限,只是在学小白一枚。代码和注释的理解难免有不当之处和错误的地方,有任何问题欢迎评论区留言指出,感激不尽!
二、目录
目录
三、项目思路
写在前面:
Java是一门面向对象的语言,我们完成这个项目的初衷不同于以前的打印九九乘法表 和 寻找水仙花数。我们的目的是将所学习的内容串起来复习,从而深入理解抽象类和接口,掌握Java语言继承多态封装的特性并将其融会贯通~
四、第一个包:book
⭐总览
📝图书类:Book
既然是图书管理系统 那我们就要面向对象 先把最基本的单位——>图书 抽象一个类出来。
package book;
//Book类,此处的Book类是对单独一本书的抽象化 将其抽象成类
//类是Java中最基本的单位了
public class Book {
//下述属性 访问权限修饰符均设置为private:封装的特性
private String name;
private String author;
private int price;
private String type;
private boolean isBorrowed;
//一本书,我们抽象出来所需要的属性无非是 书名,作者名,价格,种类,是否被借阅(借阅状态)
public Book(String name, String author, int price, String type) {
this.name = name;
this.author = author;
this.price = price;
this.type = type;
}//有参构造方法,这里的参数都是上述用到的,借阅状态不常用,所以不放入构造方法
//在IDEA编译器中,我们通常使用重写这个功能
//我们@Override之后我们在重写的方法,如果写的不对,编译器会提示我们,在编译过程就省掉了很多麻烦,所以我们一定要养成良好的习惯。
//关于重写 和 重载,面试和考试必考的重要知识点:
@Override
public String toString() {
return "Book{" + "name='" + name + '\'' + ", author='" + author + '\'' + ", price=" + price + ", type='" + type + '\'' + ", isBorrowed=" + isBorrowed + '}';
}
//下述的 10个方法 是上述 五个私有变量 对外公布的 获取和修改他们的方法
//每个变量对应两个方法:即一个构造器方法,一个访问器方法
//比如书名 name这个变量 对应的就有 getName这个访问方法 最终返回书名这个字符串
//然后setName这个方法 就可以根据我们传进去的新书名,对其进行修改
//使用IDEA编译器想要一键快速生成下面的10个方法非常简单
//我们只需要使用Alt+Shift+S这三个键一起按,然后生成构造方法即可!
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;
}
}
👍图书列表类:BookList
有了图书,我们还需要把他管理起来,想象一下当我们毕业或学期学年结束的时候,收购二手书的商人们收集的书都堆积如山了,他们成千上万本书在收购的过程中都是杂乱无章的堆起来的。我们有了上面最基本的图书类之后,我们可以用我们所抽象出来的一个类来实例化成千上万本书。但是我们能允许他们就杂乱无章的摆在那里吗?此时我们就需要一个BooKList的书架类了。你可以把这个类想象成图书馆里的书架 将其抽象出来——>BookList。
package book;
//这里的BookList类其实功能就如同其名字了
//我们抽象一个书单,也可以理解为一个书架出来
//如果学过数据结构,或者了解链表 和 线性表的 可以理解为我们的BookList这会儿就是在做一个表出来!
public class BookList {
private Book[] books = new Book[100];
//private 封装特性体现,私有修饰符,底层的书架数组只有对外看不到,保证了数据的安全性
//Book为对象(书)的类
//这个数组的设置可以理解为一个书架 是用来摆满书的
// books 是 数组名,数组内的元素都是实现Book类的实例化对象
//因为Book是抽象的,你的一本书,可以是四大名著的西游记,也可以是《Java从入门到入土~》
private int usedSize = 0;//书架的长度
public BookList() {//调用构造方法同时初始化,也就是开始往暑假里面放书了!
books[0] = new Book("三国演义", "罗贯中", 100, "小说");
books[1] = new Book("水浒传", "施耐庵", 100, "小说");
books[2] = new Book("西游记", "吴承恩", 100, "小说");
this.usedSize = 3;
}
//还是针对上述的属性: 书架:Book(即我们这个BookList类的底层本质,数组Book,用来放书)
//书架的长度 usedSize
//我们往书架里 放书 , 取书
// 可能是放一本书 ,也可能是放一堆书(直接把书架重置了)
//放书取书,如果是一本,需要确定位置,下面两个方法供使用
public void setBooks(int pos, Book book) {
this.books[pos] = book;
}
public Book getBook(int pos) {
return this.books[pos];
}
//放书取书:如果是批量操作,下面两个方法可以使用
public Book[] getBooks() {
return books;
}
public void setBooks(Book[] books) {
this.books = books;
}
public int getUsedSize() {
return usedSize;
}
public void setUsedSize(int usedSize) {
this.usedSize = usedSize;
}
}
五、第二个包:operation
⭐总览
⭐增加
package operation;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class AddOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("新增图书");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入图书的名字:");
String name = scanner.nextLine();
System.out.println("请输入图书的作者:");
String author = scanner.nextLine();
System.out.println("请输入图书的价格:");
int price = scanner.nextInt();
System.out.println("请输入图书的类型:");
String type = scanner.next();
Book book = new Book(name, author, price, type);
int curSize = bookList.getUsedSize();
bookList.setBooks(curSize, book);
bookList.setUsedSize(curSize++); //这里都封装好了,能封装的都封装好了。
System.out.println("新增成功!");
}
}
⭐借阅
package operation;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class BorrowOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("借阅图书");
//借阅图书,先想在不在,遍历这本书,看你要借的书在不在,有没有。
Scanner scanner = new Scanner(System.in);
System.out.println("请输入图书的名字:");
String name = scanner.nextLine();
for (int i = 0; i < bookList.getUsedSize(); i++) {
Book book = bookList.getBook(i);
if (book.getName().equals(name)) {
//可以借阅
book.setBorrowed(true);
System.out.println("借阅成功!");
return;
}
System.out.println("没有您要借阅的那本图书!");
}
}
}
⭐删除
package operation;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class DelOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("删除图书");
//借阅图书,先想在不在,遍历这本书,看你要借的书在不在,有没有。
Scanner scanner = new Scanner(System.in);
System.out.println("请输入图书的名字:");
String name = scanner.nextLine();
int i = 0;
for (; i < bookList.getUsedSize(); i++) {
Book book = bookList.getBook(i);
if (book.getName().equals(name)) {
break;
//可以删除
}
}
if (i == bookList.getUsedSize())//查找到头越界了也没找到
{
System.out.println("没有找到您要删除的这本书");
return;
}
//删除---》和顺序表一致
for (int pos = i; pos < bookList.getUsedSize(); pos++) //就是要插入的位置position
{
Book book = bookList.getBook(pos + 1);
bookList.setBooks(pos, book);
//在当前位置摆放上下一本书。。。
//这可不是数组类型啊宝友,这可不兴乱移动
}
bookList.setUsedSize(bookList.getUsedSize() - 1);
System.out.println("删除成功!");
}
}
⭐展示
package operation;
import book.Book;
import book.BookList;
public class DisplayOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("显示图书");
for (int i = 0; i < bookList.getUsedSize(); i++) {
Book book = bookList.getBook(i);
System.out.println(book);
}
}
}
⭐退出
package operation;
import book.BookList;
public class ExitOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("退出系统");
}
}
⭐查找
package operation;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class FindOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("查找图书");
//借阅图书,先想在不在,遍历这本书,看你要借的书在不在,有没有。
Scanner scanner = new Scanner(System.in);
System.out.println("请输入图书的名字:");
String name = scanner.nextLine();
for (int i = 0; i < bookList.getUsedSize(); i++) {
Book book = bookList.getBook(i);
if (book.getName().equals(name)) {
//可以查找到!
System.out.println(book);
System.out.println("查找成功!");
return;
}
System.out.println("没有您要查找的那本图书!");
}
}
}
⭐归还
package operation;
import book.BookList;
public class ReturnOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("归还图书");
}
}
⭐⭐⭐⭐⭐接口(需要重点理解)
package operation;
import book.BookList;
public interface IOperation//接口,接口是比抽象类更抽象的类
{
//接口是比抽象类还抽象的类
//为什么这样说呢QwQ
//因为接口内只能有抽象方法,抽象类内还能有普通方法 供子类继承
//接口的功能其实可以理解为 机械外骨骼! 单兵作战能力显著提升 配备就行了!
void work(BookList bookList);
//接口这个东西怎么说呢~~
//妙不可言的小东西 一个类 可以扩展多个接口
//然后当你多个类都扩展的有同一接口 他就妙不可言的连接在了一起 比如下面用到的数组!
}
六、第三个包:user
⭐总览
⭐普通用户
package user;
import operation.*;
import java.util.Scanner;
public class NormalUser extends User {
public NormalUser(String name) {
super(name);
this.operations = new IOperation[]{new ExitOperation(), new FindOperation(), new BorrowOperation(), new ReturnOperation()};
}
@Override
public int menu() {
System.out.println("===============");
System.out.println("Hello!~ 亲爱的" + this.name + "欢迎你!");
System.out.println("===============");
System.out.println("1:查找图书");
System.out.println("2:借阅图书");
System.out.println("3:归还图书");
System.out.println("0:退出系统");
System.out.println("===============");
Scanner input = new Scanner(System.in);
int io = input.nextInt();
return io;
}
}
⭐管理员~
package user;
import operation.*;
import java.util.Scanner;
public class Admin extends User {
public Admin(String name) {
super(name);
this.operations = new IOperation[]{//这里把操作数组放在admin里面,当我调用admin构造方法的时候
//专属于管理员的操作权限就被加载到管理员这个类所实例化的对象中了。
new ExitOperation(), new FindOperation(), new AddOperation(), new DelOperation(), new DisplayOperation()
};
}
@Override
//这个返回值类型为int的菜单方法非常巧妙
//有效的返回了 int 避免了主函数内代码冗余的同时与两种(后续开发可升级为多种)不同的权限对应不同的菜单
//满满全是细节!
public int menu() {
System.out.println("===============");
System.out.println("Hello!~ 亲爱的" + this.name + "欢迎你!");
System.out.println("===============");
System.out.println("1:查找图书");
System.out.println("2:新增图书");
System.out.println("3:删除图书");
System.out.println("4:显示所有图书");
System.out.println("0:退出系统");
System.out.println("===============");
Scanner input = new Scanner(System.in);
int io = input.nextInt();
return io;
}
}
⭐⭐⭐项目入口
package user;
import book.BookList;
import java.util.Scanner;
public class TestMain {
//方便主函数调用,返回类型 父类抽象类,向上转型大有乾坤
//我们常说的继承之后 具体执行的方法的内容和功能是跟着对象走的
//但是我们具体可以操作的(即引用类型内,此处是User类的) (可以看到的只有User类内的方法)
//我把User搞成抽象类 需要分门别类 管理员和普通用户各有千秋的方法 我写成抽象方法 子类的具体实现类必须要重写,将其完善!
//其他一致相同的,无需多言,继承父类即可!
//抽象方法的好处就体现出来了
public static User login() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入姓名:");
String name = scanner.nextLine();
System.out.println("请输入你的身份:1————>管理员,2—————>普通用户");
int choice = scanner.nextInt();
if (choice == 1) {
return new Admin(name);
} else {
return new NormalUser(name);//细节是抽象类和两个子类构造方法都有参数!
}//在此处声明对象的同时 构造类内 声明权限数组也准备好了!
}
public static boolean notkeyValue() {
Scanner scanner = new Scanner(System.in);
//此处可以用于后续升级,将用户账号密码放入数据库中!
scanner.close();
return false;
}
public static void loginTimesForThree() {
for (int i = 0; i < 3; i++) {//允许输入密码,最多输入错误三次!
if (notkeyValue()) {
System.out.println("您的账号密码输入有误,请重新输入!");
continue;
} else {
System.out.println("登陆成功!");
break;
}
}
}
public static void main(String[] args) {
//准备书籍
BookList bookList = new BookList();//即初始放入三本
//2、登录
// loginTimesForThree();
User user = login();
//动态绑定:
//此处虽然 User是抽象类,无法实例化,但是本题的巧妙之处在于通过
// login()方法中 在返回时,有return new 通过return new,记得在JAVA中,当出现new这个关键字时,
// 立马在堆上给声明的对象开辟内存。然后此处通过进行动态绑定 banding 之后 不知道是admin管理员还是普通用户
//通过输入声明对象之后 ,将 引用 赋值指向给 user 这个对象变量。用就对了!
while (true) {
int choice = user.menu();//选择输入的实际,就可以调用哪种方法了 菜单方法!
if (choice == 0) break;//break只能打断,跳出当前的一层循环!
user.doOperation(bookList, choice);//知道调用哪种操作,传入选项,传入图书列表!
}
}
}
⭐⭐⭐⭐⭐抽象类User父类
package user;
import book.BookList;
import operation.IOperation;
public abstract class User {//在此主要运用了抽象类,抽象类是因为子类都要继承他且添加更多功能!
//并且在实际开发中,我们通常不需要将父类实现,而是自顶向下,逐步求精,一步一步向下的实现
//我们在抽象过程中 一步一步的向下
//如 动物类 再往下 鸟类 再往下 鸽子类 这里面的动物类和 鸟类我们都是说不清道不明的,都是抽象的
//这也就是抽象类的由来!
protected String name;//这个就不能按照封装性由着private来了,一个protected修饰符恰到好处!
//只有默认的当前类内 和 继承他的主类 以及 当前包内可用,实际上只要包结构好,包内可用没人叼(ta)
//!!!!注意,注意,注意 这里的数组甚至向上转型了!!!
//他依靠一个接口就可以 只要实现了接口!就能往接口这个类型里放!!!
//想一想开发游戏 战士 和 法师 近战 和 远程 不同的武器装备 只要 扩展不同的接口,他的实现类就能往 接口类型的数组里面装
//比如造梦西游的孙猴子 和 三藏大法师的武器装备!
protected IOperation[] operations;//现在这个数组是空的,但是他是可以被继承的
//与此同时到时候在 管理员 admin 和 普通用户 normalUser的构造方法中加上这个声明数组变量的方法
//管理员对象 和 普通用户对象在构造的时候即激活了不同的操作权限,将其放在数组中!
//简直是美滋滋啊美滋滋。直接将对应的身份给他开辟一个数组,静态初始化更新操作数组的内容操作权限!
//抽象类 接口 YYDS!!!
public User(String name) {
this.name = name;
}//父类的有参构造方法
//不同的用户身份调用出来不同的菜单,抽象出来节省代码量,同时方便重写,且他是一个抽象类
//user是一个抽象类!!!
// 那么子类,管理员和普通用户继承 的时候,如果忘记实现,写这个方法,嘎嘎报错!
//继承之后免得再次声明,与此同时只要按照我们上述的好习惯用@Override来写,编译器看着你写,返回值类型,修饰符范围(可以缩减可以不变,但是不能扩充),想写错都难!
//此处只是声明一个 引用变量,没有给他赋值引用,他是不指向任何对象的!
//必须重写的菜单!抽象类的抽象方法,好处可见一斑!
//注意 抽象类 不能加static修饰符 static修饰符的意义就是为了和类绑定,和对象分离
//不需要实例化也能调用使用,但是你加个static 味道就变了 ,不需要对象,跟着类走,没意思了!
//你抽象类必须实例化的特性就没用了!
public abstract int menu();
//我们显式声明,这样具体实现这个User抽象类的子类,就需要先帮助父类进行构造,先传参给父类 User先有名字
//同时管理员和普通用户也必须有名字!
//抽象类不能有方法体,不能有具体操作!
public void doOperation(BookList bookList, int choice) {
this.operations[choice].work(bookList);
}
//执行操作这个方法。bolllist这个对象,你首先,选择choice要执行第几个操作,
//然后把他传进去操作数组,操作数组就调用对应的类
// protected IOperation[] operations2;
//此处这句话单独来看,因为IOperation是个接口,可以定义这个接口的数组
//接口就是比抽象类更抽象的接口。
//然后可以用接口开辟数组承装对象
}
七、总结
运行效果
本篇博客由于作者水平有限,在编写过程中内中代码和注释难免有不足之处,还请诸位读者海涵并不吝赐教,不胜感激。
本篇博客仅为阶段性学习过程中复习所作。如果有读者朋友需要无注释版的纯代码,后续我将更新。
最后,我是一名新入门的Java编程小白,喜欢的老铁不妨三连支持一下,感激不尽,抱拳了各位,我们一起学习~共同进步! 所爱隔山海,山海皆可平。
八、写在最后的最后
北方飘起了雪,瑞雪兆丰年,预祝C站各位准研究生考研顺利上岸!
现在是23点30分,还有半个小时,也预祝诸位朋友毛诞节快乐。