[ Java学习 ] 泛型类的应用实验
成文原因:
这篇文章是我这周所做的 Java 实验题的一个小总结。
这次实验让我深刻赞同了我们 Java 老师在这节实验课前告诉我们的话:
最重要的是把问题想明白,它要怎么拆分成几个类,每个类里究竟需要哪些方法,具体细节怎么实现。当你真正把这些问题想清楚了以后,剩下的编程,其实就是很简单的事情了。难度往往不是难在编程,而是分解问题为一个个小问题后,逐一解决它们的能力。
做完实验以后再来看老师这句话,觉得不能同意更多。
我贴上来的代码有两份。
前一份是未完成的代码,之所以未完成,是因为:写到一半时,觉得这样的结构设计太不合理了,如果照着这么写下去,会有许多重复的代码片段不断地出现,于是就想着这么去改进…怎样才能尽可能减少方法,但是提高代码的复用性呢?
于是,果断放弃了前一种没写完的,不太优的结构,第二份代码才是写完时真正提交的最终版本。
除了代码,我把实验报告的心得部分也一并贴了上来用以自警
-------------------------------下面是实验题目------------------------------
-------------------------------下面是代码------------------------------
/* 前一份是未完成的代码,之所以未完成,是因为:写到一半时,觉得这样的结构设计太不合理了,如果照着这么写下去,会有许多重复的代码片段不断地出现,于是就想着这么去改进…怎样才能尽可能减少方法,但是提高代码的复用性呢? 于是,果断放弃了前一种没写完的,不太优的结构,第二份代码才是写完时真正提交的最终版本。 */ import java.util.ArrayList; import java.util.LinkedList; import java.util.*; class Customer { private String name; private String fNum; // flight number,表示他所定的航线的航线号 private int sum; //买了多少票 Customer() { } Customer(String n, String f, int s) { name = n; fNum = f; sum = s; } public void setName(String n) { name = n; } public String getName() { return name; } public void setSum(int s) { sum = s; } public int getSum() { return sum; } public void setfNum(String f) { fNum = f; } String getfNum() { return fNum; } } class Flight { private static int sum = 0; //航线计数 private int order; //航线排列号 private String destination;//终点站名 private String FNum; //航班号 private int limit; //乘客限额 private int free; //余票数量 private LinkedList<Customer> booked; //已预定客户列表 private LinkedList<Customer> waiting; //等候替补客户名单 Flight() { } Flight(int o, String d, String F, int l) { order = o; destination = d; FNum = F; limit = l; } public int getOrder() { return order; } public String getDestination() { return destination; } public String getFNum() { return FNum; } public int getLimit() { return limit; } public int getFree() { return free; } public void addFree(int f) { free += f; } public void subFree(int f) { free -= f; } public LinkedList<Customer> getBooked() { return booked; } public LinkedList<Customer> getWaiting() { return waiting; } public void showInfo() //show information { System.out.println("航线排列号为:" + order + " 终点站为:" + destination); System.out.println("航班号为:" + FNum + " 成员定额为: " + limit + " 剩余票数为: " + free); System.out.println("已订票客户有:"); Iterator<Customer> it = booked.iterator(); for (; it.hasNext(); ) { System.out.println(it.next().getName() + " "); } System.out.println(); System.out.println("等候替补客户有:"); for ( it = waiting.iterator(); it.hasNext(); ) { System.out.println(it.next().getName() + " "); } System.out.println(); } } class Menu { private ArrayList<Flight> flights; Flight temp; public void init() { Flight f = new Flight(1, "北京","K2", 20); flights.add(f); f = new Flight(2, "北京","K3", 23); flights.add(f); f = new Flight(3, "成都","K4", 25); flights.add(f); } public void addFlight(Flight flight) //加入一条航线 { flights.add(flight); } public void query() //查询 { Scanner in = new Scanner(System.in); int order = in.nextInt(); //次序 Flight temp; Iterator<Flight> it = flights.iterator(); for ( ; it.hasNext(); ) { temp = it.next(); if (temp.getOrder() == order) { temp.showInfo(); return; } } System.out.println("没有检索到相关信息"); } public void book() //订票 { System.out.println("请输入航班号,您的姓名,以及您想要订购的票数"); Scanner in = new Scanner(System.in); Flight temp; String tp, tp1; //tp 为航班号,tp1为姓名 int tem; // temporary tp = in.next(); tp1 = in.nextLine(); tem = in.nextInt(); Customer c = new Customer(tp1, tp, tem); //构造函数参数依次为:姓名、 航线号、 所购票数 Iterator<Flight> it = flights.iterator(); Flight temp; for ( ; it.hasNext(); ) { temp = it.next(); if (temp.getFNum() == tp) { if (temp.getFree() >= tem) { temp.subFree(tem); temp.getBooked().add(c); System.out.println("购票成功"); } else { temp.getWaiting().add(c); System.out.println("抱歉,票数暂时不够!"); } return; } } System.out.println("输入的航线不存在!"); } public void cancel() //退票 { System.out.println("请输入航班号,您的姓名,以及您想要退票的票数"); Scanner in = new Scanner(System.in); Flight temp; String tp, tp1; //tp 为航班号,tp1为姓名 int tem; // temporary tp = in.next(); tp1 = in.nextLine(); tem = in.nextInt(); Iterator<Flight> it = flights.iterator(); Flight temp; for ( ; it.hasNext(); ) { temp = it.next(); if (temp.getFNum() == tp) //确认航班号确实存在 { for ( Iterator<Customer> it2= temp.getBooked().iterator(); it2.hasNext(); ) { Customer now = it2.next(); if (now.getName() == tp1 && now.getfNum() == tp) //确认该顾客确实定了该航班的机票 { temp.addFree(tem); System.out.println("退票成功"); if (now.getSum() == tem) //如果顾客将自己买的票全部退票,则从买票列表删除该顾客 temp.getBooked().remove(now); } } for ( Iterator<Customer> it2= temp.getWaiting().iterator(); it2.hasNext(); ) //遍历该航班的waiting列表,看是否有满足条件的购票请求可以满足 { Customer now = it2.next(); if (now.getSum() <= temp.getFree()) } } } System.out.println("输入信息有误"); } public void menu() //显示菜单 { System.out.println("----------"); System.out.println("1.航线查询"); System.out.println("2.办理退票"); System.out.println("3.办理退票"); System.out.println("4.退出系统"); System.out.println("----------"); Scanner in = new Scanner(System.in); int order = in.nextInt(); //指令 switch(order) { case 1: query(); break; case 2: book(); break; case 3: cancel(); break; case 4: System.exit(0); in.close();break; default: System.out.println("输入错误,请 重新运行订票系统!"); } menu(); } } public class test { public static void main(String args[] ) { Menu m = new Menu(); m.menu(); } }
//这份才是真正完整的,也是我提交的最终版本 import java.util.ArrayList; import java.util.LinkedList; import java.util.*; class Customer { private String name; private String fNum; // flight number,表示他所定的航线的航线号 private int sum; //买了多少票 Customer() { } Customer(String n, String f, int s) { name = n; fNum = f; sum = s; } public String getName() { return name; } public int getSum() { return sum; } public String getfNum() { return fNum; } public void setSum(int s) { sum = s; } } class Flight { private static int sum = 0; //航线计数 private int order; //航线排列号 private String destination;//终点站名 private String FNum; //航班号 private int limit; //乘客限额 private int free; //余票数量 private LinkedList<Customer> booked; //已预定客户列表 private LinkedList<Customer> waiting; //等候替补客户名单 Flight() { } Flight(int o, String d, String F, int l) { order = o; destination = d; FNum = F; limit = free = l; booked = new LinkedList<Customer>(); waiting = new LinkedList<Customer>(); } public String getDestination() { return destination; } public String getFNum() { return FNum; } public void showInfo() //show information { System.out.println("航线排列号为:" + order + " 终点站为:" + destination); System.out.println("航班号为:" + FNum + " 成员定额为: " + limit + " 剩余票数为: " + free); System.out.println(); System.out.println("已订票客户有:"); Iterator<Customer> it = booked.iterator(); for (; it.hasNext(); ) { System.out.println(it.next().getName() + " "); } System.out.println(); System.out.println("等候替补客户有:"); for ( it = waiting.iterator(); it.hasNext(); ) { System.out.println(it.next().getName() + " "); } System.out.println(); } public void isbook(Customer c) { if (c.getSum() > free) //检验是否有足够余票可供该顾客订票 { System.out.println("抱歉,票数暂时不足!"); waiting.add(c); return; } book(c); //先判断可不可以订票,确认可以再订票 } public void book(Customer c) //这个函数可重用,在退票以后,遍历 waiting 链表之前 { for (Iterator<Customer> it = booked.iterator(); it.hasNext(); ) //判断该顾客之前是否有订过该航班的票 { Customer now = it.next(); if (now.getName().equals(c.getName())) { now.setSum(now.getSum() + c.getSum()); free -= c.getSum(); System.out.println("顾客" + c.getName() + "订票成功"); System.out.println("一共成功订票" + now.getSum() + "张,航班号为" + FNum ); return; } } booked.add(c); free -= c.getSum(); System.out.println("顾客" + c.getName() + "订票成功"); System.out.println("一共成功订票" + c.getSum() + "张,航班号为" + FNum ); } public void cancel(Customer c) { Iterator<Customer> it = booked.iterator(); boolean found = false; for ( ; it.hasNext(); ) { Customer now = it.next(); if (now.getName().equals(c.getName())) { found = true; free += c.getSum(); System.out.println("退票成功"); if (now.getSum() == c.getSum())//如果顾客将自己买的票全部退票,则从买票列表删除该顾客 { booked.remove(now); break; } } } if (!found) System.out.println("退票失败,您没有定该航班的机票!"); System.out.println(); it = waiting.iterator(); for ( ; it.hasNext(); ) { Customer now = it.next(); if (now.getfNum().equals(FNum) && now.getSum() <= free) book(now); } } } class Menu { private ArrayList<Flight> flights; Flight temp; public void init() { flights = new ArrayList<Flight>(); Flight f = new Flight(1, "北京","k2", 20); addFlight(f); f = new Flight(2, "北京","k3", 23); addFlight(f); f = new Flight(3, "成都","k4", 25); addFlight(f); } public void addFlight(Flight flight) //加入一条航线 { flights.add(flight); } public void query() //查询 { System.out.println("请输入要查询的目的地城市:"); Scanner in = new Scanner(System.in); String d = in.next(); Flight temp; Iterator<Flight> it = flights.iterator(); boolean found = false; for ( ; it.hasNext(); ) { temp = it.next(); if (temp.getDestination().equals(d)) { found = true; temp.showInfo(); } } if (!found)System.out.println("没有检索到相关信息"); } public void book() //订票 { Scanner in = new Scanner(System.in); System.out.println("请输入订票人数:"); int t = in.nextInt(); Flight temp; // 航班类临时变量 String tp, tp1; //tp 为航班号,tp1为姓名 int tem; // temporary for (int i = 0; i < t; i++) { System.out.println("请输入航班号,您的姓名,以及您想要订购的票数"); tp = in.next(); tp1 = in.next(); tem = in.nextInt(); Customer c = new Customer(tp1, tp, tem); //构造函数参数依次为:姓名、 航线号、 所购票数 Iterator<Flight> it = flights.iterator(); boolean found = false; for ( ; it.hasNext(); ) { temp = it.next(); if (temp.getFNum().equals(tp)) { temp.isbook(c); found = true; break; } } if (!found) System.out.println("输入的航线不存在!"); System.out.println(); } } public void cancel() //退票 { Scanner in = new Scanner(System.in); System.out.println("请输入退票人数:"); int t = in.nextInt(); Flight temp; // 航班类临时变量 String tp, tp1; //tp 为航班号,tp1为姓名 int tem; // temporary for (int i = 0; i < t; i++) { System.out.println("请输入航班号,您的姓名,以及您想要退票的票数"); tp = in.next(); tp1 = in.next(); tem = in.nextInt(); Customer c = new Customer(tp1, tp, tem); //构造函数参数依次为:姓名、 航线号、 所购票数 Iterator<Flight> it = flights.iterator(); boolean found = false; for ( ; it.hasNext(); ) { temp = it.next(); if (temp.getFNum().equals(tp)) //确认航班号确实存在 { temp.cancel(c); found = true; break; } } if (!found) System.out.println("输入信息有误"); System.out.println(); } } public void menu() //显示菜单 { System.out.println("----------"); System.out.println("1.航线查询"); System.out.println("2.办理订票"); System.out.println("3.办理退票"); System.out.println("4.退出系统"); System.out.println("----------"); Scanner in = new Scanner(System.in); int order = in.nextInt(); //指令 switch(order) { case 1: query(); break; case 2: book(); break; case 3: cancel(); break; case 4: System.exit(0); in.close();break; default: System.out.println("输入错误,请重新运行订票系统!"); } menu(); } } public class test { public static void main(String args[] ) { Menu m = new Menu(); m.init(); m.menu(); } }
-------------------------------这些是实验心得体会------------------------------
五、心得体会(要详细,编程中碰到的问题及解决方案)
搜过的资料(都是超链接,可直接点击)
Java中关于nextInt()、next()和nextLine()的理解
eclipse中如何去除警告:Class is a raw type. References to generictype Class<T> should be parameterized
解决“List is a raw type. References to generic type List”提示的问题
java泛型问题 关于警告:XXis a raw type【转】
java出现Resourceleak: 'XXX' is never closed 解决方案
java.lang.NullPointerException 空指针异常问题
改进和思考:
1. 在写 Menu类 的 book() 和 cancel() 函数时,发现其实有大量重叠部分,因为 cancel() 函数在删除完以后,也是要遍历该航线的 Waiting列列表,看有没有 Customer 对象能够完成之前没能完成的订票。也就是说,book()函数里已经实现过的功能,还需要在 cancel()函数里在实现一次。
这样导致的结果,就是代码看上去又拖沓又冗长,于是想了个办法,在航班类里加入 book() 和 cancel() 函数,并且将 Customer类对象作为参数传到航班类里,这样就能直接调用 Flight 类的函数来完成订票退票,可以提高代码的复用性。
而且,如果传入 Customer类对象,那么,其实航班类的很多get函数都可以直接去掉了,一下子使代码简洁多了,看起来终于舒服多了,之前的一堆get函数很是拖沓…
从这个优化的过程感受到的就是:
不要急着敲代码,而是先想想能不能有更好的思路,比如这题,刚开始时就没有想到特别透彻清楚明白的程度,导致后来,实现函数的时候才发现,完全不用那么繁琐,明明有更简单的方法来实现,我为什么要弄得那么复杂?
所以,先别急着敲代码,而是应该先把每一个细节琢磨清楚,多问问自己:还能不能再简化一点?能不能再把代码写优美一点?一拿到题目就做,往往是最浪费时间的方法,因为后面用来修改和优化的时间会更多。我应该做的是,每次把所有优化的可能考虑周全,尽量把类里的方法减少到最少,同时提高这些方法的利用率,我觉得这才是实验里最为关键的部分。
2. 着手于简化、简化再简化
将不必要的get 和 set 函数都去掉,例如 Customer类,该题不考虑顾客改名的情况,故而不需要 name 的 set方法;此外,该题没有一个顾客同时买多个航线的飞机票的情况,就算真的要处理这种情况,按照我的代码设计的逻辑,也是要新建一个 Customer 对象的,因为我觉得这样做更方便处理。
再说详细一些就是,如果aaa同时买了 K2 和 K3 航班的票,那么,我打算采取的,不是将这这个航班的购票数都存在一个 Customer的对象里,而是打算再建立一个 Customer对象,保存 K3 的航班线名臣和K3的订票数。因为在办理订票和退票时,我们都是根据航班号来查航线的。如果把不同的航班号,存在同一个 Customer对象里,其实无形中加大了查找特定某航线的难度。
所以,照着这么说,航线名也是不会改的,如果这个顾客买的某个航线的票,全都被他自己后来退光了,那也只需要在那个航线的 booked 链表里删掉这个顾客的信息即可
这么想来,其实 fNum 的 set 方法也可以去掉,因为就本题的考虑,根本用不到这个方法。
唯一需要保留的 set 方法,是sum的set方法,因为可能存在退票但没有退完的情况,这时就需要改变 Customer对象的订票数了。
同理,对于 Flight 类,也应进行一次这样的考虑和排查。因为方法是为了解决问题而设计的,不是为了写一个方法而去写一个方法。所以,如果能更简单地解决问题,就不要把问题弄复杂了!~
3. 情况考虑要比题目设置更加周全一些:
题目好像没提到一点,其实在进行订票时,也应该进行一轮该航班的 booked 列表的遍历。
因为,考虑真实情况时,同一个顾客是可以多次订票的,所以,我们需要先知道顾客之前有没有订过票,订过和没订过的处理方式是不同的。
4. 语法点和知识不太熟练,导致的失误
4.1. 在进行字符串比较时,又忘了两个字符串之间是不能直接用 == 来判断相等的,而是应该用 equals 函数,又一次把 C++的语法规则套在Java上用了…说明我学的还是不到位啊!
4.2. 此外,用于读取 String对象的 next 和 nextLine,必须弄清楚两者的区别,正确选择用哪个,否则用了 equals 来比较以后,得到的结果仍然是不相等
4.3. 使用泛型前,要先对其进行初始化,否则会出现空指针错误
-------------------------------其他相关文章------------------------------
[ Java学习 ] 类的其他文章汇总(都是超链接,可直接点击):
[ Java学习 ] 破除思维定势之C++ 和 Java 的差异001
[ Java学习 ] 破除思维定势之C++ 和 Java 的差异002
[ Java学习 ] 破除思维定势之C++ 和 Java 的差异003
[ Java学习 ] “goto语句“和 “continue + 标号”的不同待遇
[ Java学习 ] toString方法 和equals方法