用 Java 模拟一个图书馆。包括创建图书、创建读者、借书、还书、列出所有图书、 列出所有读者、列出已借出的图书、列出过期未还的图书等功能。每个读者最多只能借 3 本书,每个书最多只能借 3 个星期,超过就算过期。
下面是一个命令行下的实现。这个例子的主要目的是向初学者展示内部类的好处。 Command 及其子类都是 LibrarySimulaTor 的内部类。它们可以无阻碍的访问 LibrarySimulaTor 的成员。使用内部类,而不是大量的 if-else,让程序更容易扩展。
01.import java.io.BufferedReader;02.import java.io.IOException;03.import java.io.InputStreamReader;04.import java.text.SimpleDateFormat;05.import java.util.*;06.07./**08. * 一个图书馆的课程设计。主要功能:09. * 1. 创建图书10. * 2. 创建读者11. * 3. 借书12. * 4. 还书13. * 5. 列出所有书14. * 6. 列出已借书15. * 7. 列出超过日期未还的书16. */17.public class LibrarySimulaTor {18.19. // 主菜单20. private static final String MAIN_MENU = "1. 列出所有的书/n" +21. "2. 列出已借出的书/n" +22. "3. 列出过期未还的书/n" +23. "4. 列出所有读者/n" +24. "5. 创建图书/n" +25. "6. 创建读者/n" +26. "7. 借书/n" +27. "8. 还书/n" +28. "9. 退出/n" +29. "请输入序号:";30.31. // 选择图书类型的菜单。在借书和添加图书的时候都会用到32. private static final String TYPE_MENU;33.34. // 表示一个数字的正则表达式35. private static final String DIGIT_CHOICE_PATTERN = "^//d$";36.37. // 表示非空字符串38. private static final String NOT_EMPTY_PATTERN = "//S.*";39.40. // 日期格式41. static final String DATE_PATTERN = "yyyy/MM/dd";42.43. // 验证用户输入日期的正则表达式44. static final String DATE_FORMAT_PATTERN = "^//d{4}///d{2}///d{2}$";45.46. // 预定义的图书类型47. static HashMap<String, String> TYPES = new LinkedHashMap<String, String>();48.49. static {50. TYPES.put("1", "科学类");51. TYPES.put("2", "文学类"); // 新的类别可以继续在后面添加52. TYPE_MENU = createTypeMenu();53. }54.55. // 生成选择类别的菜单56. private static String createTypeMenu() {57. String str = "";58. for (String index : TYPES.keySet()) {59. str += index + ". " + TYPES.get(index) + "/n";60. }61. return str + "请选择书的类型:";62. }63.64.65. private HashMap<Integer, Command> commands = new HashMap<Integer, Command>();66.67. private ArrayList<Book> books = new ArrayList<Book>();68.69. private ArrayList<Reader> readers = new ArrayList<Reader>();70.71. // 程序入口。这里创建一个 LibrarySimulaTor 用于模拟界面。72. public static void main(String[] args) {73. new LibrarySimulaTor().start();74. }75.76. /**77. * 构造函数78. */79. public LibrarySimulaTor() {80. commands.put(1, new Command1());81. commands.put(2, new Command2());82. commands.put(3, new Command3());83. commands.put(4, new Command4());84. commands.put(5, new Command5());85. commands.put(6, new Command6());86. commands.put(7, new Command7());87. commands.put(8, new Command8());88. }89.90. /**91. * 这里接受用户输入,执行操作,然后再等待用户输入,这样不停的循环。92. */93. private void start() {94. String index = prompt(MAIN_MENU, DIGIT_CHOICE_PATTERN);95.96. while (!index.equals("9")) {97. executeCommand(index);98. index = prompt(MAIN_MENU, DIGIT_CHOICE_PATTERN);99. }100. }101.102. // 根据序号执行命令103. private void executeCommand(String index) {104. Command command = commands.get(Integer.parseInt(index));105. if (command != null) {106. String result = command.execute();107. System.out.println(result + "/n");108. }109. }110.111. // 打印一条提示信息,然后读取并返回用户输入112. private String prompt(String message, String pattern) {113. System.out.print(message);114. if (pattern == null) {115. return readInput();116. } else {117. String result = "";118. while (!result.matches(pattern)) {119. result = readInput();120. }121. return result;122. }123. }124.125. // 读取用户输入126. private String readInput() {127. try {128. BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));129. return reader.readLine();130. } catch (IOException e) {131. e.printStackTrace();132. return "";133. }134. }135.136. // 根据名字查找读者。找不到则返回 null。137. private Reader getReaderByName(String readerName) {138. for (Reader reader : readers) {139. if (reader.getName().equals(readerName)) {140. return reader;141. }142. }143. return null;144. }145.146. // 根据名字查找图书。找不到则返回 null。147. private Book getBookByName(String bookName) {148. for (Book book : books) {149. if (book.getName().equals(bookName)) {150. return book;151. }152. }153. return null;154. }155.156. /*===================================================================*/157.158. /**159. * 代表命令的抽象类160. */161. private abstract class Command {162.163. protected abstract String execute();164. }165.166. /////////////////////////////////////////////////// 列出所有图书167. private class Command1 extends Command {168.169. protected String execute() {170. for (Book book : getBooks()) {171. System.out.println(book); // 这里会自动调用 book.toString()172. }173. return "命令完成。";174. }175.176. private ArrayList<Book> getBooks() {177. ArrayList<Book> result = new ArrayList<Book>();178.179. for (Book book : books) {180. if (isValid(book)) {181. result.add(book);182. }183. }184. return result;185. }186.187. // 考虑到第 1、2、3 条命令大体相同,这里提供了一个给子类覆写的方法188. protected boolean isValid(Book book) {189. return true;190. }191. }192.193. ///////////////////////////////////////////////////// 列出已借出的书。194. // 注意它的父类不是 Command,而是 Command1。这样节省了很多重复代码195. private class Command2 extends Command1 {196.197. @Override198. protected boolean isValid(Book book) {199. return book.isBorrowed();200. }201. }202.203. //////////////////////////////////////////////////////// 列出过期未还的书204. private class Command3 extends Command1 {205.206. @Override207. protected boolean isValid(Book book) {208. // 判断一本书接触过期与否的方法最好在 Book 类中去实现。209. return book.isExpired();210. }211. }212.213. /////////////////////////////////////////////// 创建图书214. private class Command5 extends Command {215.216. protected String execute() {217. String type = getType();218. String name = getName();219. if (getBookByName(name) == null) {220. books.add(new Book(type, name));221. return "图书添加成功。";222. } else {223. return "图书添加失败:名称已存在。";224. }225. }226.227. // 获得用户输入的书名228. private String getName() {229. return prompt("请输入书名:", NOT_EMPTY_PATTERN);230. }231.232. // 获得用户选择的图书类型233. private String getType() {234. return prompt(TYPE_MENU, DIGIT_CHOICE_PATTERN);235. }236. }237.238. /////////////////////////////////////////////////////// 列出所有读者239. private class Command4 extends Command {240.241. protected String execute() {242. for (Reader reader : readers) {243. System.out.println(reader);244. }245. return "命令完成。";246. }247. }248.249. /////////////////////////////////////////////////////// 创建读者250. private class Command6 extends Command {251.252. protected String execute() {253. String name = getName();254. if (getReaderByName(name) == null) {255. readers.add(new Reader(name));256. return "读者创建成功。";257. } else {258. return "读者创建失败:名字已经存在。";259. }260. }261.262. public String getName() {263. return prompt("请输入读者名字:", NOT_EMPTY_PATTERN);264. }265. }266.267. /////////////////////////////////////////////////////// 借书268. private class Command7 extends Command {269.270. protected String execute() {271. Reader reader = getReader();272. if (reader == null) {273. System.out.println("命令取消。");274. return "";275. }276.277. Book book = getBook();278. if (book == null) {279. System.out.println("命令取消。");280. return "";281. }282.283. String borrowDate = getBorrowDate();284.285. book.borrowBy(reader.getName(), borrowDate);286. reader.addBorrowCount();287.288. return "成功借出。";289. }290.291. private String getBorrowDate() {292. String now = new SimpleDateFormat(LibrarySimulaTor.DATE_PATTERN).format(new Date());293. String date = null;294. while (date == null || !date.matches(DATE_FORMAT_PATTERN)) {295. date = prompt("请输入结束日期(如" + now + ")", NOT_EMPTY_PATTERN);296. }297. return date;298. }299.300. private Book getBook() {301. Book book = null;302. while (book == null || book.isBorrowed()) {303. String bookName = prompt("请输入图书名字:", null);304. if (bookName.equals("")) {305. return null;306. }307.308. book = getBookByName(bookName);309. if (book == null) {310. System.out.println("图书不存在。");311. } else if (book.isBorrowed()) {312. System.out.println("图书已经被借出。");313. }314. }315. return book;316. }317.318. private Reader getReader() {319. Reader reader = null;320. while (reader == null || !reader.canBorrow()) {321. String readerName = prompt("请输入读者名字:", null);322. if (readerName.equals("")) {323. return null;324. }325.326. reader = getReaderByName(readerName);327. if (reader == null) {328. System.out.println("读者不存在。");329. } else if (!reader.canBorrow()) {330. System.out.println("该读者已经借了" + Reader.MAX_BORROW + " 本书,不能继续借了。");331. }332. }333. return reader;334. }335. }336.337. ///////////////////////////////////////////// 还书338. private class Command8 extends Command {339.340. protected String execute() {341. Reader reader = getReader();342. if (reader == null) {343. System.out.println("命令取消。");344. return "";345. }346.347. Book book = getBook(reader);348. if (book == null) {349. System.out.println("命令取消。");350. return "";351. }352.353. reader.reduceBorrowCount();354. book.returned();355. return "操作成功。";356. }357.358. private Book getBook(Reader reader) {359. Book book = null;360. while (book == null || !reader.getName().equals(book.getBorrower())) {361. String bookName = prompt("请输入图书名字:", null);362. if (bookName.equals("")) {363. return null;364. }365.366. book = getBookByName(bookName);367. if (book == null) {368. System.out.println("图书不存在。");369. } else if (!reader.getName().equals(book.getBorrower())) {370. System.out.println("该读者没有借出这本书。");371. }372. }373. return book;374. }375.376. private Reader getReader() {377. Reader reader = null;378. while (reader == null) {379. String readerName = prompt("请输入读者名字:", null);380. if (readerName.equals("")) {381. return null;382. }383.384. reader = getReaderByName(readerName);385. if (reader == null) {386. System.out.println("读者不存在。");387. }388. }389. return reader;390. }391. }392.}393.394.// 图书395.class Book {396.397. public static final int EXPIRE_DAYS = 21; // 可借出天数,超过就算过期398.399. private String type;400.401. private String name;402.403. private String borrowedBy = null;404.405. private String borrowDate = null;406.407. Book(String type, String name) {408. this.type = type;409. this.name = name;410. }411.412. @Override413. public String toString() {414. String str = String.format("类别:%s 书名:%s", LibrarySimulaTor.TYPES.get(type), name);415. if (isBorrowed()) {416. str += " 借出人:" + borrowedBy + " 借出时间:" + borrowDate;417. }418. return str;419. }420.421. public boolean isBorrowed() {422. return borrowedBy != null;423. }424.425. public String getName() {426. return name;427. }428.429. public String getBorrowDate() {430. return borrowDate;431. }432.433. /**434. * 图书借出435. *436. * @param name 读者名字437. * @param date 借出日期。格式:参见 {@link LibrarySimulaTor#DATE_PATTERN}438. */439. public void borrowBy(String name, String date) {440. this.borrowedBy = name;441. this.borrowDate = date;442. }443.444. public boolean isExpired() {445. if (borrowDate == null) {446. return false; // 没有借出的书不出现在过期未还列表当中,所以这里返回 false。447. }448.449. // 从当前时间往前推 3 个星期,如果还在借书日期之后,说明借书已经超过 3 个星期了450. String threeWksAgo = get3WeeksAgo();451. return threeWksAgo.compareTo(borrowDate) > 0;452. }453.454. // 获得 3 个星期前的日期455. private String get3WeeksAgo() {456. SimpleDateFormat f = new SimpleDateFormat(LibrarySimulaTor.DATE_PATTERN);457. Calendar c = Calendar.getInstance();458. c.add(Calendar.DAY_OF_MONTH, -EXPIRE_DAYS);459. return f.format(c.getTime());460. }461.462. public void returned() {463. this.borrowBy(null, null);464. }465.466. public String getBorrower() {467. return borrowedBy;468. }469.}470.471.// 读者472.class Reader {473.474. // 每位读者最多可同时借出 3 本书475. public static final int MAX_BORROW = 3;476.477. private String name;478.479. private int borowCount = 0;480.481. public int getBorowCount() {482. return borowCount;483. }484.485. Reader(String name) {486. this.name = name;487. }488.489. public String getName() {490. return name;491. }492.493. public void addBorrowCount() {494. borowCount++;495. }496.497. public void reduceBorrowCount() {498. borowCount--;499. }500.501. public boolean canBorrow() {502. return borowCount < MAX_BORROW;503. }504.505. @Override506. public String toString() {507. return name;508. }509.}
送给中意的TA,背面写上:某年某月某日,