学习了类和对象之后,就可以自己来做个小的项目来提升自己的能力了。本文章就来说明如何来做一个简单的图书管理系统。写一些小项目是一种很锻炼逻辑和熟悉语法的好方法。
在做这些项目的时候,要明白一点是我们的Java是面向对象的,要使用面向对象的思想来完成。先想想大概需要什么对象,比如书,用户等。不要想着一步就能想到全部的内容,要慢慢来,按照逻辑的过程写出代码。
下面标题中有(1)(2)之类的说明还没有写完或者需要写出其他部分的代码才能继续这部分的代码。截图没有截包的导入,报错的时候大多是包没有导入,要记着导入包。
图书管理系统,图书是一定需要的,要创建一个图书类,包括图书的书名、作者名、书的单价、书的类型、是否借出图书。用户也是一定需要的,所以要创建一个用户类。现在为了增加难度,我想要设置两个用户类:一个是管理员类,可以让管理员对图书进行管理,管理员对图书进行增删查改;一个就是普通的用户类,借阅归还图书。同时,两个类对应的菜单是不相同的。其他的内容就在写代码的时候在进行补充。
为了更好的管理代码,贴近实际开发,我们要使用Java中的包,同时每一个Java文件中只写一个类。
main方法是所有程序的入口,所以要先创建。
创建了之后,不知道要写什么,就先放着,把知道的写出来。
在idea中新建一个Book包,用来存放图书相关的类。
创建一个Book类来定义书。
按照我们前面的想法,使用Java的封装的特点,用私有化的权限定义一些书的属性。这些属性创建之后,需要进行初始化,可以使用构造方法,也可以使用Setter和Getter方法。我们最好两个都写上,在后面的使用中就比较方便一点。同时,重写Book类中的toString方法,方便打印。
public class Book {
private String bookName;
private String authorName;
private double price;
private String type;
private boolean isBorrowed;
public Book(String bokName, String authorName, double price, String type) {
this.bookName = bokName;
this.authorName = authorName;
this.price = price;
this.type = type;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bokName) {
this.bookName = bokName;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public double getPrice() {
return price;
}
public void setPrice(double 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{" +
"bookName='" + bookName + ''' +
", authorName='" + authorName + ''' +
", price=" + price +
", type='" + type + ''' +
(isBorrowed ? " 已借出" : " 未借出") +
'}';
}
}
现在是单个的书类创建好了。但是图书馆中,并不是一个图书,而是很多的图书排成一排。这个时候,就想到了数组。创建一个Book类的数组,用来模拟这个书架。想一想,放在Book类里面是不合理的,所以要新创建一个类,用来管理图书。
创建一个BookList类来定义书架,在BookList类里面定义一个Book类的数组,还应该定义一个变量表示有多少本书。因为一个书架上不一定是满书的状态。在构造方法里面定义BooK数组大小为5个,初始化3本书。
BookList里面需要什么方法呢?获取书架上某一个位置的书、改变书架上某一个位置的书、获取书架上书的数量、当书减少或增加时改变书的数量。
public class BookList {
private Book[] books;
private int BookUseSize;
public BookList() {
books = new Book[5];
books[0] = new Book("三国演义", "施耐庵", 68.8, "小说");
books[1] = new Book("水浒传", "罗贯中", 56.6, "小说");
books[2] = new Book("西游记", "吴承恩", 60, "小说");
this.BookUseSize = 3;
}
/**
* 设置书
* 用来设置改变某个下标的书,
* @param i 某个书的下标
* @param book 书
*/
public void setBooks(int i, Book book) {
if (i >= books.length) {
expansion();
}
books[i] = book;
}
/**
* 得到pos下标的书
* 返回某个下标的书的信息
* @param i 请求的下标
* @return Book类型,包含书的成员
*/
public Book getPosBooks(int i) {
return books[i];
}
/**
* 设置书可用范围大小
* 设置书对象的大小
* @param i 书对象的大小
*/
public void setBooksUseSize(int i) {
this.BookUseSize = i;
}
/**
* 得到书可用范围大小
* 返回书对象大小
* @return 书对象的大小
*/
public int getBooksUseSize() {
return this.BookUseSize;
}
public void expansion() {
books = Arrays.copyOf(books, 2 * books.length);
}
}
书的包到这里就结束了。
接下来,就要写用户相关的类。把这些类放在一个包中。
管理员类和普通用户类都有一些共性:用户姓名、菜单等,所以可以把它们共有的抽取出来,形成继承关系。
public abstract class User {
protected String userName;
public User(String userName) {
this.userName = userName;
}
}
管理员类继承父类User类。
public class Administrator extends User {
public Administrator(String userName) {
super(userName);
}
}
普通用户类继承了父类User类。
public class GeneralUser extends User {
public GeneralUser(String userName) {
super(userName);
}
}
然后,你会说,就这么简单?当然不是。按照我们的逻辑继续往下走。
通过这个图,我们看到,是管理员还是普通用户,是需要自己来选择的。创建好了两个用户类,就可以把它们进行实例化来使用。在哪里进行实例化?这个时候就可以使用前面写的Main方法了。它是程序的入口,从Main方法中实例化不同的对象进行不同的选择是在适合不过的。
在继续这部分的代码。我们前面说过,普通用户和管理员用户对应的菜单是不相同的。普通用户查找、借阅、归还图书;管理员需要新增、查找、删除图书,所以,不同的对象对应不同的菜单。这样我们想起来多态。User类和它的子类是继承关系,可以发生向上转型,进而发生多态。
Main方法中写出能够选择不同身份的代码。要注意发生多态部分的代码。
import Book.BookList;
import User.User;
import User.Administrator;
import User.GeneralUser;
import java.util.Scanner;
public class Main {
public static User login() {
System.out.println("请输入你的姓名:>");
Scanner scanner = new Scanner(System.in);
String userName = scanner.nextLine();
while (true) {
System.out.println("请输入你的身份:> 1,管理员 0,普通用户");
int choice = scanner.nextInt();
if (choice == 1) {
return new Administrator(userName);//发生多态
} else if (choice == 0) {
return new GeneralUser(userName);//发生多态
} else {
System.out.println("选择错误,请重新选择:>");
}
}
}
public static void main(String[] args) {
User user = login();
}
}
选择身份之后,就要面临不同的菜单,返回去在User包中完成这部分的代码。
public class Administrator extends User {
public Administrator(String userName) {
super(userName);
}
public int menu() {
System.out.println("====hello," + super.userName + " welcome====");
System.out.println("1,查找图书");
System.out.println("2,新增图书");
System.out.println("3,删除图书");
System.out.println("4,显示图书");
System.out.println("0,退出系统");
System.out.println("请输入你的操作:>");
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
}
public class GeneralUser extends User {
public GeneralUser(String userName) {
super(userName);
}
public int menu() {
System.out.println("====hello," + super.userName + " welcome====");
System.out.println("1,查找图书");
System.out.println("2,借阅图书");
System.out.println("3,归还图书");
System.out.println("0,退出系统");
System.out.println("请输入你的操作:>");
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
}
子类中都含有menu方法。不同的子类的具体实现是不一样的。在前面的Main方法中,是使用User发生多态来调用不同的对象的,所以,一定会使用User来调用不同的menu方法,而父类没有这个方法,就要加上menu方法,但是可以没有具体的实现。换句话来说,这个menu方法就成了一个抽象方法。这个User类,成了一个抽象类,抽象类最大的作用就是被继承。一个普通类转变成了一个抽象类!
public abstract class User {
protected String userName;
public User(String userName) {
this.userName = userName;
}
public abstract int menu();//不要忘记了子类中的重写标记
}
现在写完后,有一个问题是,如何通过菜单上不同的选择去使用不同的操作呢?我们先去写出操作的大致框架,在来思考这个问题。
在idea中创建一个Operation包,这个包中的类用来完成用户和图书之间的交互。如果创建正确,将会是这样:
用户和图书之间之间的操作,总的有管理员的对图书新增,删除等操作,普通用户对图书借阅和归还等操作。
把这些操作,分别写成类。这些类中的实现到后面来实现。以AddOperation类来举例,其他的类相同。
每一个操作,都会有一个work方法,所以,我们可以写一个接口,来表现这些work的统一。同时,把这些写成一个接口还有另一个好处,在后面说明。
现在回到User包中,去解决如何通过菜单上不同的选择去使用不同的操作的问题。
如何根据不同的身份的不同的菜单选择去实现不同的操作呢?而且管理员和普通用户的菜单是不同的。要根据不同的选择去实例化不同的操作对象。我们可以去写一个数组,这样就能够达到我们的期望。那么,创建一个什么样的数组?这个数组里面应该存放这些操作的对象的引用,那么数据类型是什么?是一个引用类型。这就要用到我们前面的接口IOperation了。AddOperation等类实现了接口IOperation。当然,用IOperation接口就是图个方便,使用其他的抽象类或接口也是可以的。
在User类中定义一个IOperation类型的数组但是不写具体的实现,在子类中的构造方法中写出具体的实现。
下面,就是User包中的所有代码。
package User;
import Operation.*;
import java.util.Scanner;
public class Administrator extends User {
public Administrator(String userName) {
super(userName);
this.iOperations = new IOperation[] {
new ExitOperation(),
new FindOperation(),
new AddOperation(),
new DelOperation(),
new DisplayOperation()
};
}
@Override
public int menu() {
System.out.println("====hello," + super.userName + " welcome====");
System.out.println("1,查找图书");
System.out.println("2,新增图书");
System.out.println("3,删除图书");
System.out.println("4,显示图书");
System.out.println("0,退出系统");
System.out.println("请输入你的操作:>");
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
}
package User;
import Operation.*;
import java.util.Scanner;
public class GeneralUser extends User {
public GeneralUser(String userName) {
super(userName);
this.iOperations = new IOperation[] {
new ExitOperation(),
new FindOperation(),
new BorrowOperation(),
new ReturnOperation()
};
}
@Override
public int menu() {
System.out.println("====hello," + super.userName + " welcome====");
System.out.println("1,查找图书");
System.out.println("2,借阅图书");
System.out.println("3,归还图书");
System.out.println("0,退出系统");
System.out.println("请输入你的操作:>");
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
}
上面的代码,解决了菜单对应的操作,但是还没有解决如何将具体的具体的选择和操作结合起来。
在User类中,新增一个doOperation方法,能够将选择和具体的操作结合起来。因为IOperation是一个数组,所以通过下标来实例化不同的操作。同时,这些操作时针对书架操作的,而不是书,还要传进去BookList。
package User;
import Book.BookList;
import Operation.IOperation;
public abstract class User {
protected String userName;
public User(String userName) {
this.userName = userName;
}
public IOperation[] iOperations;
public abstract int menu();
public void doOperation(int userChoice, BookList bookList) {
iOperations[userChoice].work(bookList);
}
}
在main方法中调用login方法来实现身份的选择。选择身份后,调用doOperation方法来实现不同的操作。这是Main类的全部实现。
import Book.BookList;
import User.User;
import User.Administrator;
import User.GeneralUser;
import java.util.Scanner;
public class Main {
public static User login() {
System.out.println("请输入你的姓名:>");
Scanner scanner = new Scanner(System.in);
String userName = scanner.nextLine();
while (true) {
System.out.println("请输入你的身份:> 1,管理员 0,普通用户");
int choice = scanner.nextInt();
if (choice == 1) {
return new Administrator(userName);
} else if (choice == 0) {
return new GeneralUser(userName);
} else {
System.out.println("选择错误,请重新选择:>");
}
}
}
public static void main(String[] args) {
User user = login();
BookList bookList = new BookList();
while (true) {
int userChoice = user.menu();
user.doOperation(userChoice, bookList);
}
}
}
写完了这些,就是剩下的具体操作了。
按照提示,输入书名、作者名等,然后实例化一个书对象并进行初始化。在调用bookList中的方法把这本书新增到书架上。新增的时候,检查书架容量够不够,不够的话就要进行扩容。
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("==新增图书==");
addBookOperation(bookList);
}
public void addBookOperation(BookList bookList) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入书名:>");
String bookName = scanner.nextLine();
System.out.println("请输入作者名:>");
String authorName = scanner.nextLine();
System.out.println("请输入书的类型:>");
String type = scanner.nextLine();
System.out.println("请输入书的单价:>");
Double price = scanner.nextDouble();
Book book = new Book(bookName, authorName, price, type);
int currentSize = bookList.getBooksUseSize();
bookList.setBooks(currentSize, book);
bookList.setBooksUseSize(currentSize + 1);
System.out.println("新增成功!");
}
}
循环打印书。要注意的是我们对书进行了封装,所以不能直接通过下标来打印。要通过公开的方法去打印。
package Operation;
import Book.BookList;
public class DisplayOperation implements IOperation{
@Override
public void work(BookList bookList) {
System.out.println("==展示图书==");
displayBooksOperation(bookList);
}
public void displayBooksOperation(BookList bookList) {
int currentSize = bookList.getBooksUseSize();
for (int i = 0; i < currentSize; i++) {
System.out.println(bookList.getPosBooks(i));
}
}
}
这个类中的查找方法很常用。按照书名或者作者名进行查找。同时,为了方便在其他的类中使用查找方法,在这里对查找方法findBooksOperation()进行了重载。
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 scan = new Scanner(System.in);
System.out.println("请输入查找的方式:> 1,按书名查找 0,按作者查找");
int find = scan.nextInt();
scan.nextLine();
String[] howToFind = new String[] {"作者名", "书名"};
System.out.println("请输入你要查找的" + howToFind[find] +":>");
String bookName = scan.nextLine();
int result = findBooksOperation(bookList, bookName, find);
if (result == -1) {
System.out.println("未找到该书!");
return;
}
System.out.println(bookList.getPosBooks(result));
}
public int findBooksOperation(BookList bookList, String bookName, int mark) {
int currentSize = bookList.getBooksUseSize();
for (int i = 0; i < currentSize; i++) {
Book book = bookList.getPosBooks(i);
if (mark == 1 && book.getBookName().equals(bookName)) {
return i;
}
if (mark == 0 && book.getAuthorName().equals(bookName)) {
return i;
}
}
return -1;
}
public int findBooksOperation(BookList bookList, String bookName) {
int currentSize = bookList.getBooksUseSize();
for (int i = 0; i < currentSize; i++) {
Book book = bookList.getPosBooks(i);
if (book.getBookName().equals(bookName)) {
return i;
}
}
return -1;
}
}
这两个类需要调用到FindOperation类中的findBooksOperation()方法。
package Operation;
import Book.BookList;
import Book.Book;
import java.util.Scanner;
public class BorrowOperation implements IOperation{
@Override
public void work(BookList bookList) {
System.out.println("==借阅图书==");
borrowBookOperation(bookList);
}
public void borrowBookOperation(BookList bookList) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要借阅的书名:>");
String borrowBookOperation = scanner.nextLine();
FindOperation findOperation = new FindOperation();
int findResult = findOperation.findBooksOperation(bookList, borrowBookOperation);
if (findResult == -1) {
System.out.println("没有此书信息!");
return;
}
Book book = bookList.getPosBooks(findResult);
book.setBorrowed(true);
System.out.println("已借出!");
}
}
package Operation;
import Book.Book;
import Book.BookList;
import java.util.Scanner;
public class ReturnOperation implements IOperation {
@Override
public void work(BookList bookList) {
System.out.println("==归还图书==");
returnBookOperation(bookList);
}
public void returnBookOperation(BookList bookList) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要归还的书名:>");
String returnBookOperation = scanner.nextLine();
FindOperation findOperation = new FindOperation();
int findResult = findOperation.findBooksOperation(bookList, returnBookOperation);
if (findResult == -1) {
System.out.println("没有此书信息!");
return;
}
Book book = bookList.getPosBooks(findResult);
book.setBorrowed(false);
System.out.println("已归还!");
}
}
这个操作比较难一些,调用FindOperation类中的findBooksOperation()方法找到书后,采用从后往前覆盖的方式来完成删除。最后一个就置为null。
package Operation;
import Book.BookList;
import Book.Book;
import java.util.Scanner;
public class DelOperation implements IOperation{
@Override
public void work(BookList bookList) {
System.out.println("删除图书");
delBookOperation(bookList);
}
public void delBookOperation(BookList bookList) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入要删除的书名:>");
String delName = scan.nextLine();
FindOperation find = new FindOperation();
int result = find.findBooksOperation(bookList, delName);
if (result == -1) {
System.out.println("没有此书信息!");
return;
}
int currentSize = bookList.getBooksUseSize();
int i = 0;
for (i = result; i < currentSize - 1; i++) {
Book book = bookList.getPosBooks(i + 1);
bookList.setBooks(i, book);
}
bookList.setBooks(i,null);
bookList.setBooksUseSize(currentSize - 1);
System.out.println("已删除!");
}
}
在退出系统之前,将每一个书对象置为null。
package Operation;
import Book.BookList;
public class ExitOperation implements IOperation{
@Override
public void work(BookList bookList) {
System.out.println("==退出系统==");
System.out.println("你已经退出");
System.exit(0);
int currentSize = bookList.getBooksUseSize();
for (int i = 0; i < currentSize; i++) {
bookList.setBooks(i, null);
}
}
}
虽然我们完成了图书管理系统部分的代码,但是这个系统的逻辑还是不完善的。想完善的可以自己去完善。这个小项目虽然小,但是在实现的过程中确实是挺锻炼初学者的,比如按照不同的菜单去调用不同的操作的方法的实现。回顾整个过程,并不是一下子就很顺利的写完的,所以在写之前,要打一个大概的框架,在写的过程中完善代码。