------------恢复内容开始------------
一、作业总结
我们总共进行了三次题目集的练习,无论是题目量,题目难度还是知识点,都是逐步递增的,并且越进行到后面的题目,运用到的技术也越来越多,在一周内(并且还有其他的课业的情况下)完成还是颇具难度的。
第一次题目集总体难度并不大,主要考察的是对输入输出最基本的运用,更多的还是面向过程的运用,只有最后一题判断三角形需要考虑的多一点。
第二次题目集相比与第一次,难度就有着一定的提升了,第二次的四题开始考察类与方法的运用,慢慢开始有着面向对象的影子了。
第三次题目的难度与前两次相比,难度就开始直线上升了,代码的耦合度开始迅速上升,开始运用继承,多态以及接口等,对于这次题目,我也想了很久才做出来,期中遇到的问题也是最多的,调试测试点的过程也是很痛苦,不过好在最后也是成功钻研了出来。
二、设计与分析
主要分析菜单计价程序1,菜单计价程序2和菜单计价程序3:
菜单计价程序一
菜品类:对应菜谱上一道菜的信息。
Dish
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
点菜记录类:保存订单上的一道菜品记录
Record
Dish d;//菜品
int portion;//份额(1/2/3代表小/中/大份)
int getPrice()//计价,计算本条记录的价格
订单类:保存用户点的所有菜的信息。
Order
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(String dishName,int portion)
//添加一条菜品信息到订单中。
输入格式:
每条点菜记录的格式:
菜名+空格(英文)+份额
注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
最后一条记录以“end”结束。
输出格式:
订单上所有菜品的总价(整数数值),每份菜
如果订单中包含不能识别的菜名,则在总价之前输出“ does not exist”,是不能识别的菜名
类图
方法图
菜单计价系统1:main函数通过调用 菜品类Dish 菜谱类Menu 点菜记录类Record 订单类Order 4个类来完成相应功能,然后创建一个菜谱对象和一个订单对象,接受的一整行的输入,将这一行沿空格进行分割,将相应的份数转换为浮点数,再将点菜的菜名与菜谱对象进行比较,检查对应的菜名,
运行结果如下:
代码如下:import java.util.*;
import java.util.Scanner;
// 菜品类
class Dish
String name; // 菜品名称
int unit_price; // 单价
Dish(String name, int unit_price)
this.name = name;
this.unit_price = unit_price;
// 计算菜品价格的方法
int getPrice(int portion)
double price = unit_price;
if (portion == 2)
price *= 1.5;
else if (portion == 3)
price *= 2;
return (int) Math.round(price);
// 菜谱类
class Menu
Dish[] dishes; // 菜品数组,保存所有菜品信息
// 构造函数
Menu(Dish[] dishes)
this.dishes = dishes;
// 根据菜名在菜谱中查找菜品信息
Dish searchDish(String dishName)
for (Dish dish : dishes)
if (dish.name.equals(dishName))
return dish;
return null;
// 点菜记录类
class Record
Dish dish; // 菜品
int portion; // 份额(1/2/3代表小/中/大份)
// 构造函数
Record(Dish dish, int portion)
this.dish = dish;
this.portion = portion;
// 计价,计算本条记录的价格
int getPrice()
return dish.getPrice(portion);
// 订单类
class Order
List records; // 保存订单上每一道的记录
// 构造函数
Order()
this.records = new ArrayList<>();
Record addARecord(String dishName, int portion, Menu menu)
Dish dish = menu.searchDish(dishName);
if (dish == null)
System.out.println(dishName + " does not exist");
return null;
Record record = new Record(dish, portion);
records.add(record);
return record;
int getTotalPrice()
int totalPrice = 0;
for (Record record : records)
totalPrice += record.getPrice();
return totalPrice;
// 主函数
public class Main
public static void main(String[] args)
// 创建菜谱
Dish[] dishes =
new Dish("西红柿炒蛋", 15),
new Dish("清炒土豆丝", 12),
new Dish("麻婆豆腐", 12),
new Dish("油淋生菜", 9)
;
Menu menu = new Menu(dishes);
// 创建订单
Order order = new Order();
// 读取点菜记录
Scanner scanner = new Scanner(System.in);
while (true)
String line = scanner.nextLine();
if (line.equals("end"))
break;
String[] parts = line.split(" ");
String dishName = parts[0];
int portion = Integer.parseInt(parts[1]);
order.addARecord(dishName, portion, menu);
// 输出总价
System.out.println(order.getTotalPrice());
菜单计价系统二:
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。
菜单由一条或多条菜品记录组成,每条记录一行
每条菜品记录包含:菜名、基础价格 两个信息。
订单分:点菜记录和删除信息。每一类信息都可包含一条或多条记录,每条记录一行。
点菜记录包含:序号、菜名、份额、份数。
份额可选项包括:1、2、3,分别代表小、中、大份。
删除记录格式:序号 delete
标识删除对应序号的那条点菜记录。
不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格2。
如果计算出现小数,按四舍五入的规则进行处理。
参考以下类的模板进行设计:
菜品类:对应菜谱上一道菜的信息。
Dish
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
Dish addDish(String dishName,int unit_price)//添加一道菜品信息
点菜记录类:保存订单上的一道菜品记录
Record
int orderNum;//序号
Dish d;//菜品
int portion;//份额(1/2/3代表小/中/大份)
int getPrice()//计价,计算本条记录的价格
订单类:保存用户点的所有菜的信息。
Order
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(int orderNum,String dishName,int portion,int num)//添加一条菜品信息到订单中。
delARecordByOrderNum(int orderNum)//根据序号删除一条记录
findRecordByNum(int orderNum)//根据序号查找一条记录
输入格式:
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:
序号+英文空格+菜名+英文空格+份额+英文空格+份数
注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
最后一条记录以“end”结束。
输出格式:
按顺序输出每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品*份数,序号是之前输入的订单记录的序号。
如果订单中包含不能识别的菜名,则输出“** does not exist”,**是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后输出订单上所有菜品的总价(整数数值),
UML图如下:
运行结果如下:
代码如下:
import java.util.Scanner;
public class Main
public static void main(String[] args)
Scanner input = new Scanner(System.in);
Menu menu = new Menu();
Order order = new Order();
Tranform tranform = new Tranform();
String s;
String name;
int price;
int portion;
int id;
int a;
int dishsum;
while(true)
s = input.nextLine();
String[] split = s.split(" "); //分割符操作
if(split[0].equals("end"))
break;
a = tranform.getsize(split);
if(a1)
name = split[0];
price = Integer.parseInt(split[1]);
menu.addDish(name,price);
else if(a2)
id = Integer.parseInt(split[0]);
name = split[1];
portion = Integer.parseInt(split[2]);
dishsum = Integer.parseInt(split[3]);
Dish dish = menu.searthDish(name);
if(dishnull)
System.out.println(name+" does not exist");
else
System.out.println(id+" "+name+" "+dish.getprice(portion,dishsum));
order.addARecord(id,dish,portion,dishsum);//添加订单
else if(a3)
id = Integer.parseInt(split[0]);
Record record = order.findRecordByNum(id);
if (record == null)
System.out.println("delete error");
else
order.delARecordByOrderNum(id);
int sum=0;
sum= order.getTotalPrice(); //计算总和
System.out.println(sum);
class Dish
String name;//菜品名称
int price; //单价
public Dish(String name,int price)
this.name = name;
this.price = price;
public int getprice(int portion,int dishsum)
if(portion1)
return price*dishsum;
else if(portion2)
int Iprice = (int) (price1.5);
if(price-Iprice>=0.5)
return (Iprice+1)dishsum;
else
return Ipricedishsum;
else if(portion==3)
return price2*dishsum;
return 0;
//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份;
class Menu
Dish[] dishs = new Dish[100];
public Dish searthDish(String dishName)
for(int i=0;i<dishs.length;i++)
if(dishs[i]!=null && dishs[i].name.equals(dishName))
return dishs[i];
return null;
public Dish addDish(String dishName,int price)
Dish dish = new Dish(dishName,price);
for(int i=0;i<dishs.length;i++)
if(dishs[i]==null)
dishs[i] = dish;
break;
return dish;
class Order
Record[] records = new Record[100];//保存订单上每一道的记录
public int getTotalPrice()
int sum = 0;
for(int i=0;i<records.length;i++)
if(records[i]!=null)
records[i].getPrice();
sum+=records[i].getPrice();
return sum;
public Record addARecord(int id,Dish dish,int portion,int dishsum)
//Menu menu = new Menu();
//Dish dish = menu.searthDish(dishName);
Record record = new Record(dish,portion,dishsum);
records[id] = record;
return record;
//添加一条菜品信息到订单中。
public Record findRecordByNum(int id)
return records[id];
public Record delARecordByOrderNum(int id)
records[id]=null;
return null;
class Record
int dishsum;
Dish d;//菜品
int portion;//份额(1/2/3代表小/中/大份)
public Record(Dish dish, int portion,int dishsum)
this.dishsum = dishsum;
this.d=dish;
this.portion=portion;
int getPrice()
int price = d.getprice(portion,dishsum);
return price;
//计价,计算本条记录的价格
class Tranform
public int getsize(String[] split)
String id = split[0];
String[] arr = "1", "2", "3", "4", "5", "6", "7", "8", "9";
for (int i = 0; i < 9; i++)
if (id.substring(0, 1).equals(arr[i]))
String cd = split[1];
if (cd.equals("delete"))
return 3;
else
return 2;
return 1;
菜单计价系统三:
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。
菜单由一条或多条菜品记录组成,每条记录一行
每条菜品记录包含:菜名、基础价格 两个信息。
订单分:桌号标识、点菜记录和删除信息、代点菜信息。每一类信息都可包含一条或多条记录,每条记录一行或多行。
桌号标识独占一行,包含两个信息:桌号、时间。
桌号以下的所有记录都是本桌的记录,直至下一个桌号标识。
点菜记录包含:序号、菜名、份额、份数。份额可选项包括:1、2、3,分别代表小、中、大份。
不同份额菜价的计算方法:小份菜的价格=菜品的基础价格。中份菜的价格=菜品的基础价格1.5。小份菜的价格=菜品的基础价格2。如果计算出现小数,按四舍五入的规则进行处理。
删除记录格式:序号 delete
标识删除对应序号的那条点菜记录。
如果序号不对,输出"delete error"
代点菜信息包含:桌号 序号 菜品名称 份额 分数
代点菜是当前桌为另外一桌点菜,信息中的桌号是另一桌的桌号,带点菜的价格计算在当前这一桌。
程序最后按输入的先后顺序依次输出每一桌的总价(注意:由于有代点菜的功能,总价不一定等于当前桌上的菜的价格之和)。
每桌的总价等于那一桌所有菜的价格之和乘以折扣。如存在小数,按四舍五入规则计算,保留整数。
折扣的计算方法(注:以下时间段均按闭区间计算):
周一至周五营业时间与折扣:晚上(17:00-20:30)8折,周一至周五中午(10:30--14:30)6折,其余时间不营业。
周末全价,营业时间:9:30-21:30
如果下单时间不在营业范围内,输出"table " + t.tableNum + " out of opening hours"
参考以下类的模板进行设计:菜品类:对应菜谱上一道菜的信息。
Dish
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
Dish addDish(String dishName,int unit_price)//添加一道菜品信息
点菜记录类:保存订单上的一道菜品记录
Record
int orderNum;//序号\\
Dish d;//菜品\\
int portion;//份额(1/2/3代表小/中/大份)\\
int getPrice()//计价,计算本条记录的价格\\
订单类:保存用户点的所有菜的信息。
Order
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(int orderNum,String dishName,int portion,int num)//添加一条菜品信息到订单中。
delARecordByOrderNum(int orderNum)//根据序号删除一条记录
findRecordByNum(int orderNum)//根据序号查找一条记录
输入格式:
桌号标识格式:table + 序号 +英文空格+ 日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:序号+英文空格+菜名+英文空格+份额+英文空格+份数注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
代点菜信息包含:桌号+英文空格+序号+英文空格+菜品名称+英文空格+份额+英文空格+分数
最后一条记录以“end”结束。
输出格式:
按输入顺序输出每一桌的订单记录处理信息,包括:
1、桌号,格式:table+英文空格+桌号+”:”
2、按顺序输出当前这一桌每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品*份数,序号是之前输入的订单记录的序号。如果订单中包含不能识别的菜名,则输出“** does not exist”,**是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后按输入顺序一次输出每一桌所有菜品的总价(整数数值)格式:table+英文空格+桌号+“:”+英文空格+当前桌的总价
本次题目不考虑其他错误情况,如:桌号、菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的后续作业中会做要求。
输入格式:
桌号标识格式:table + 序号 +英文空格+ 日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
菜品记录格式:
菜名+英文空格+基础价格
如果有多条相同的菜名的记录,菜品的基础价格以最后一条记录为准。
点菜记录格式:序号+英文空格+菜名+英文空格+份额+英文空格+份数注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
删除记录格式:序号 +英文空格+delete
代点菜信息包含:桌号+英文空格+序号+英文空格+菜品名称+英文空格+份额+英文空格+分数
最后一条记录以“end”结束。
输出格式:
按输入顺序输出每一桌的订单记录处理信息,包括:
1、桌号,格式:table+英文空格+桌号+“:”+英文空格
2、按顺序输出当前这一桌每条订单记录的处理信息,
每条点菜记录输出:序号+英文空格+菜名+英文空格+价格。其中的价格等于对应记录的菜品*份数,序号是之前输入的订单记录的序号。如果订单中包含不能识别的菜名,则输出“** does not exist”,**是不能识别的菜名
如果删除记录的序号不存在,则输出“delete error”
最后按输入顺序一次输出每一桌所有菜品的总价(整数数值)格式:table+英文空格+桌号+“:”+英文空格+当前桌的总价
UML图:
首先引入一个时间类:
import java.time.LocalDateTime;
以下是time类代码:
class Time
String time1;
String time2;
int year;
int month;
int day;
int hour;
int minute;
int weekday;
void getWeekday()
this.weekday= LocalDateTime.of(this.year,this.month,this.day,this.hour,this.minute).getDayOfWeek().getValue();
void getYear()
String [] k=time1.split("\\/");
year=Integer.parseInt(k[0]);
month=Integer.parseInt(k[1]);
day=Integer.parseInt(k[2]);
void getDay()
String[] k = time2.split("\\/");
hour=Integer.parseInt(k[0]);
minute=Integer.parseInt(k[1]);
这里的时间类就是将传入的字符串按顺序分割,获得是在星期几的几点。然后在table里计算价格的时候使用。
以下为table类代码:
class Table
int num;
Time time=new Time();
Order order=new Order();
int tablePrice;void getPrice()
time.getDay();
time.getYear();
time.getWeekday();
if(time.weekday<=5&&time.weekday>=1)
if((time.hour>=17&&time.hour<20)||(time.hour==20&&time.minute<=30))
tablePrice=(int)Math.round(order.getTotalPrice()*0.8);
System.out.print("table "+this.num+": "+this.tablePrice+" ");
else if((time.hour==10&&time.minute>=30)||(time.hour>=11&&time.hour<14)||(time.hour==14&&time.minute<=30))
tablePrice=(int)Math.round(order.getTotalPrice()*0.6);
System.out.print("table "+this.num+": "+this.tablePrice+" ");
else System.out.println("table "+this.num+" out of opening hours");
if(time.weekday==6||time.weekday==7)
if((time.hour==9&&time.minute>=30)||(time.hour>9&&time.hour<21)||(time.hour==21&&time.minute<=30))
tablePrice=(int)Math.round(order.getTotalPrice());
System.out.print("table "+this.num+": "+this.tablePrice+" ");
else System.out.println("table "+this.num+" out of opening hours");
这里我在获得价格的同时直接输出结果(对获得的时间进行判断然后计算相应折扣),所以在主函数直接调用就行,而且这里一个桌子就代表着一个订单,则在内部再定义一个order类。还有一个桌号的属性。
主函数main就需要重新写过,因为每个字符串的长度发生了变化,所以出现的情况会有所不同,main函数代码如下:
public static void main(String[] args)
Scanner sc=new Scanner(System.in);
Table[] table=new Table[10];
Menu menu=new Menu();
int i=0;
String str=sc.nextLine();
while(!str.equals("end"))
String[] data = str.split(" ");
if(data.length==2&&!data[1].equals("delete"))
menu.add(data[0],Integer.parseInt(data[1]));
if(data.length==4&&data[0].equals("table"))
i++;
table[i]=new Table();
table[i].order=new Order();
table[i].order.menu=menu;
table[i].num=Integer.parseInt(data[1]);
table[i].time.time1=data[2];
table[i].time.time2=data[3];
System.out.println("table "+Integer.parseInt(data[1])+":");
if(data.length==4&&!data[0].equals("table"))
if(menu.searthDish(data[1])!=null)
table[i].order.addARecord(Integer.parseInt(data[0]), data[1], Integer.parseInt(data[2]), Integer.parseInt(data[3]));
Dish d = new Dish();
d=menu.searthDish(data[1]);
System.out.println(data[0]+" "+data[1]+" "+d.getPrice(Integer.parseInt(data[2]))*Integer.parseInt(data[3]));
else if(menu.searthDish(data[1])==null)
System.out.println(data[1]+ " does not exist");
if(data.length==5)
if(menu.searthDish(data[2])!=null)
table[i].order.addARecord(Integer.parseInt(data[1]),data[2],Integer.parseInt(data[3]),Integer.parseInt(data[4]));
Dish d = new Dish();
d=menu.searthDish(data[2]);
System.out.println(data[1]+" "+"table"+" "+table[i].num+" pay for table "+data[0]+" "+d.getPrice(Integer.parseInt(data[3]))*Integer.parseInt(data[4]));
else if(menu.searthDish(data[2])==null)
System.out.println(data[2]+ " does not exist");
if(data.length==2&&data[1].equals("delete"))
table[i].order.delARecordByOrderNum(Integer.parseInt(data[0]));
str=sc.nextLine();
for(int num=1;num<=i;num++)
table[num].getPrice();
sc.close();
由于第三次的类图与uml图较为混乱且有缺陷,在此就不做展示了。
三、踩坑心得
在实现第三道题目中,最困难的是维护数量不确定的订单中的菜品,特别是在添加订单记录时需要动态地增加记录,在Java中可以使用数组实现。同时,在写查询单个菜品的接口时,
需要注意到可能返回null值的情况,必须进行判断以避免程序崩溃。除此之外,整个程序的设计并没有太大的问题,实现起来比较顺畅。
在写代码时,最重要的是规划,如果一个for循环就可以解决的问题,不必用更多的for循环而导致运行超时。
四、主要困难与踩坑建议
应让代码更加符合规范。此外,尽可能的将方法简化,将重复代码提出,减少重复代码,提高代码的复用性。
SouranceMonitor的生成报表图可知,有简单明了的注释,在自己写代码的过程中也能够让自己读懂,方便前后增添、修改代码。除代码的注释外,对变量的命名也要规范,要使用简单易懂,具有意义的名称,使代码易于理解与维护。
五、总结
java中的已经定义的类是非常多的,仅仅是写题目的时候用一次只能有一个粗略映像,需要花其他时间去巩固。此外,在写题目时,不要急于求成,也不要不带脑子写代码,使得写出的代码不仅得分不高,质量还低