第二次博客作业:第四、五次大作业及期中考试总结性Blog
第四、五次大作业及期中考试总结性Blog
相比于前面的PTA大作业题集来说,第四、五次大作业只有一题菜单的迭代,虽然题目量不多,但是难度却是大大增加,更加考验大家的学习自觉性和Java的功底啦!这两次的大作业均是在菜单系列-3的基础上迭代,是菜单系列-3的两个不同分支。自然而然,与菜单系列-3的代码结构的完整性和可复用性等关联就很大,菜单系列-3设计的好可以为后续的这两个迭代分支大大减少麻烦,节约时间、降低难度。
菜单系列-3的圈复杂度:显而易见我的菜单系列-3的圈复杂度为18(虽然很大程度上是Judge判断时间以此确定折扣和是否在营业时间的代码模块的if-else调用级数过多的原因,但是也离不开代码结构设计的问题),这也就导致了后续迭代的两次大作业写的很艰难。
后面我尝试了用正则表达式,但是正则表达式依然解决不了这个全复杂度的问题。当然,可以将其拆分成多个子方法,但是感觉这样好麻烦也没有必要,就没有这么操作。因为这块判断时间的代码在后续可以直接调用,并不需要说用到其中的某部分时间判定的代码块(比如说,这个代码模块判断的是年-月-日-时-分-秒,在后续功能需求中并未有需要我们去说只判断年或者月这类的需求)。
菜单系列-4功能需求分析:
菜单系列-4是菜单系列-3的异常情况的迭代:主要就是新增了异常情况的错误判定。
在刚开始着手本次大作业的时候,并未考虑太多,只是单纯的用if-else来进行错误判断;但是就立马碰壁了,写到时间的错误判断时if-else的级数调用实在是太多层了,程序的可读性很差,每次回顾这段代码时,即使有注释都需要逐级理解。所以后面就改用了正则表达式判定,不仅大大提高了代码的可读性,也大幅减少了代码量。
菜单系列-5功能需求分析:
菜单系列-5与菜单系列-4同属于菜单系列-3的两个不同方向的迭代分支:菜单系列-5主要是添加了口味度的判断以及单用户多桌菜,多用户多桌菜的情况。
难点是代点菜是价格加到本桌,而口味度加到被点桌。在删除时在删除本桌该条记录价格的同时,还要删除被点桌的口味度值。
菜单系列
一、菜单系列-4
类图:

圈复杂度:

难度:难
经过前面几次的大作业和上次的Blog分析总结,我想也不用去讲各种方法、库函数、概念的分析了,所用到的知识都是前面PTA题集里用过的。所以,直接讲代码设计思路吧!
知识储备正则表达式:(43条消息) Java正则表达式(一看就懂)_陶然同学的博客-CSDN博客
以下是我的正则表达式的使用示例:



设计与分析
详解各个类的属性和方法:
Dish、Menu、Record、Order类在上次Blog作业中已经进行过阐述,本次作业中这几个类也只是做了略微的改动,就不再详细赘述,水字数啦!
以下主要介绍新增的Table和Judge类设计与分析:
桌类:记录当前桌的订单及相关信息
Table{
Order order = new Order();
int year=0,month=0,day=0,ww=0,hh=0,mm=0,ss=0;
int tableNum;//桌号
String time;//点餐时间
int discount;//记录当前时间的折扣
int T_discount = 10;
int before_sum_price = 0;//折扣前的总价
int SumPrice = 0;//当前桌总价
boolean work = false;//判断当前是否在营业状态,1表示在营业
}
判断类:进行异常情况的判断
Judge{
boolean menuTime_error = true;
boolean tableNum_time_error = true;
boolean validTime = true;
boolean tableNum = true;//[1-55]
boolean judge(String time1,String time2,Table table)//判断是否在营业时间
boolean Judge_tableNum(String[] arr,String input_data)//判断桌号信息是否有误
boolean Judge_record(String input_data) //判断点菜记录是否错误
boolean Judge_menu(String input_data)//判断菜单信息是否错误
boolean Judge_validTime(String[] arr)//判断桌号时间是否有误
boolean sameTime(Table a,Table b)//判断两桌是否在同一时间段
}
以下简单分享以下我的设计思路——在菜单系列-3的代码基础上,通过if-else的条件语句判断是否为异常情况,若是则输出对应的提示信息:输入桌号时,调用Judge类中的方法Judge_tableNum。①先在菜谱类中初始化菜品信息;②在主方法中循环输入,通过调用Order订单类中的addARecord方法,添加订单消息;调用Judge类中的Judge_record方法。③addARecord方法中调用Menu菜谱类中的searthDish方法,用于设置Record类种的属性d,d中包含当前点菜记录所点菜品的份额和单价。若当前所点的菜品菜品不存在,则将单价设置为0;调用Judge类中的Judge_menu方法。④循环结束后,先遍历订单消息,查找各条点菜记录中菜品对应单价是否为0。若为0,则输出菜品不存在的对应提示信息“菜名+does not exist”;⑤最后,调用Order类中的getTotalPrice方法计算总价并输出。getTotalPrice方法中调用Record类中的方法getPrice,调用Judge类中的Judge_validTime方法和 sameTime,再计算本条记录的价格,Record类中的方法getPrice实际上并不执行计价功能,而是通过调用Dish类中的getPrice实现计价功能。
这题的难点就在于如何设计思路,完成各种各样的错误判断。在有了方向之后,将思路通过代码编写出来并不困难,代码量也不算长,容易出错的地方可能就是非零返回的问题。需要特别注意的是,对于各类异常情况需要考虑完全,否则就有可能导致无法通过测试点!
以下通过点菜记录和菜谱记录的异常情况示例加以说明:
|
if(arr_length == 4) |
|
ok_Menu = judge_table.Judge_menu(input_data); |
踩坑点+我的Bug:
Ⅰ、代点菜测试点:
代点菜测试点中,大多数同学可能在设计这段代码时,并未考虑到代点菜删除时的情况。
我的方案:在循环遍历删除时将份数改为0,同时记录该条点菜记录删除次数,防止重复删除。在开始时我并不是让“records[i].num = 0”而是将该条点菜记录的价格改为0,这也就导致后面计算价格时出现错误。
|
void findRecordByNum(int orderNum,int cnt)//根据序号查找一条记录 |
Ⅱ、输入数据时,单词中有两个连续的空格:这种情况不能按照一个空格来分割,按照一个空格分隔时,会导致字符串分隔后长度加1;所以需要按照“一个或两个空格分隔”
| String[] arr=a[i].split(" | "); |
Ⅲ、同一时间段的判定,以及合并。重复的桌号信息如果两条信息的时间在同一时间段,此时输出结果时合并点菜记录统一计价。
代码展示:
| int searchtime(table[]t,table a,int sumtable)//若找到返回对应桌,找不到返回下一个空桌 { //(时段的认定:周一到周五的中午或晚上是同一时段,或者周末时间间隔1小时(不含一小时整,精确到秒) for(int i=0;i<sumtable;i++) { if(a.tablenum==t[i].tablenum) if(a.year==t[i].year) { if(a.day==t[i].day) { if(a.ww>=1&&a.ww<=5)//周1~5 { if(a.hh<=14&&t[i].hh<=14||a.hh>14&&t[i].hh>14)//在同一时段 { return i; } } else//周末 { if(Math.abs((a.hh-t[i].hh)*60*60+(a.mm-t[i].mm)*60)+(a.ss-t[i].ss)<60*60)//在同一时段 { return i; } } } } } return sumtable; } |
源码展示:
|
public static void main(String[] args) { if(tables[i_table].work == false)//当前不在营业时间 } |
二、菜单系列-5
类图:

圈复杂度:第一版打的代码过于“屎山”,这是重新设计更新后的代码的圈复杂度

第一版圈复杂度:高达18(但是这多了个星号,我也不知道是什么意思,总之这版非常的“屎山”)

难度:偏难
客观来讲这次的大作业虽然与菜单系列-4同属于菜单系列-3的分支,但是这次难度应该比菜单系列-3的稍微简单点。可悲的是,因为我自己写的代码比较“堆屎山”,导致后面的两个测试点一直通过不了——“单用户-多桌菜-含delete”和“单用户—多菜系—含错误记录”,因此后面我用HashMap,Arraylist,HashSet等重新打了一版,顿时感觉高级好用多了,直接得到升华。
知识点
以下附上我学习HashMap,Arraylist,HashSet这些知识点的链接:
HashMap: (43条消息) Java集合之一—HashMap_woshimaxiao1的博客-CSDN博客
Arraylist: (43条消息) Arraylist的基本使用方法_两手空空!的博客-CSDN博客
HashSet: (43条消息) Java中哈希集(HashSet)概念,实现以及操作_java hashset_Sueko的博客-CSDN博客
设计与分析
详解关键类的属性与方法
Order,Record,Dish,Menu类与菜单系列-4大同小异,不再过多赘述!
信息类:通过map存储用户信息,方便后续调用,可以大大减少代码量
class Data
{
//static Map<String,man>allman=new HashMap<>();//所有人·,键值为人名
static Map<String,man> allman = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
static Map<Integer,table>t=new HashMap<>();
static Menu c=new Menu();
}
输入类:判定输入数据的各类情况,包括异常情况
class input{
static void judgement(String input_data);//判断输入内容
}
用户类:通过List存储用户数据,用户名下可以有多桌订单
class man
{
String name;//用户名
String phone_number;//电话号码
int sum=0;//总价
List<Integer>tablenum=new ArrayList<>();
// table 1 : tom 13605054400 2023/5/6 12/30/00
//约束条件:客户姓名不超过10个字符,
//手机号11位,前三位必须是180、181、189、133、135、136其中之一。
static void newman(String x);//判定是否为新用户
//table 1 : tom 13605054400 2023/5/1 18/30/00
void addtable(int n);//判定是否要添加桌
void getttprice();//计算价格
static void show()//打印结果
}
桌类:每桌新增了一个订单类——吃的订单+点的订单;方便计算价格,和进行删除操作。
class table
{
int tablenum;//桌号
String time;//点菜时间
int year=0,month=0,day=0,ww=0,hh=0,mm=0,ss=0;
boolean flag=true;//判断时间是否正确
double count=0,specialcount=0;//折扣
Order eatorder=new Order();//吃的订单
Order buyorder=new Order();//点的订单
int sum=0,truesum1=0;//计算总价
double truesum=0;
int a1=0,b1=0,c1=0;//记录菜系对应菜分数
int a2=0,b2=0,c2=0;//总辣栓甜度
static int n=0;//记录当前处理桌号
static boolean newtable(String a)//添加桌
void input(String time)//预处理
void getTotalkoweidu()//计算总价
void timechange()//时间转换
static void show();//打印结果
}
以下对Menu,Order,Record类中有改动的方法进行简要说明
class Menu类//菜谱类
方法addDish---传入输入的菜名,菜品单价
数据类型:dishName--主方法中输入的菜名,赋值给菜谱数组中的菜品名称(dish[].name)
unit_price--主方法中输入的菜价,赋值给菜谱数组中的菜品单价(dish[].unit_price)
处理:将传入(或者说输入)的菜名和单价通过return返回值的形式带回给Menu类(菜谱类)中的dishs数组(菜谱)
方法searthDish---传入菜名,序号,菜谱
参数:dishName--输入的菜名,用于和菜谱中的菜名比对(new_menu.dishs[i].name)
name_count--表示菜谱中的菜品总数,用于控制循环次数
new_menu--菜谱
处理:根据菜名在菜谱中查找菜品信息,返回Dish对象
class Record//点菜记录类
数据类型:orderNum--该条点菜记录的序号
num--该条点菜记录的菜品份数
portion--该条点菜记录的菜品份额
d--菜品类的对象,用于存储菜品名称和菜价
方法getprice---传入输入的菜名,菜品单价
数据类型:dishName为主方法中输入的菜名,赋值给菜谱数组中的菜品名称(dish[].name)
unit_price为主方法中输入的菜价,赋值给菜谱数组中的菜品单价(dish[].unit_price)
处理:计价,计算本条记录的价格--通过调用菜品类对象d的方法找到菜价,再乘以份额得到该条点菜记录的价格
完成单挑点菜记录价格和订单中包含不能识别的菜名的输出
class Order//订单类
数据类型:sum_price--用于该类的方法getTotalPrice中,记录菜品总价
delete_error_cnt--用于该类的方法findRecordByNum中,记录错误删除的次数
records[]--订单数组
方法getTotalPrice---传入订单总数
参数:参数为int--订单总数,用于控制循环次数
处理:循环遍历,通过调用订单类records[i]的方法getPrice计算每条订单价钱,累加到sum_price中,返回订单总价
方法addARecord---传入序号、菜名、份额、份数、菜谱中的菜品总数、菜谱
参数:dishName--菜名,传入调用的菜谱类的方法searthDish中
n--菜谱中的菜品总数,传入调用的菜谱类的方法searthDish中
menu--菜谱、传入调用的菜谱类的方法searthDish中
portion--份额
orderNum--序号
处理:通过调用菜谱类menu的方法searthDish的return返回设置点菜记录中菜品类对象d
完成添加一条菜品信息到订单中
方法findRecordByNum---传入序号,订单信息总数
处理:计算错误删除的次数
设计与分析
Ⅰ、在本次菜单中,新增了用户概念,同一用户可能点了多桌菜,每条菜品也都新增了口味都这一概念,所以考虑到功能中涉及到代点菜的时,需要把价格算在本桌,而口味度算在被点桌;删除代点菜订单时,不仅需要在本桌删除该条订单价格,还需要删除被点桌的口味度。可想而知,如果每个桌只有一个Order类,那么在计算总价和进行删除操作时就会非常的麻烦,逻辑也会非常的混乱。因此我在每个Table类中都设计了两个Order订单类——吃的订单和点的订单。
①、点的订单包括自己桌点的以及给其他桌代点的订单,在计算总价时遍历点的订单即可;
②、吃的订单包括自己给自己点的,以及其他桌给自己点的,在计算口味度时遍历吃的订单即可;
③、删除操作:无论删除的是代点菜的还是普通点菜的订单记录,都只需将吃的订单和点的订单都遍历,删除本条订单记录即可。
Ⅱ、题目中有用户这一概念,那么自然而然就会想到新增一个man的用户类了。这个类中并没有太多的值得说道的东西,下面简单介绍一下。
①、属性:name——用户姓名;phone_number——电话号码;sun——总价
②、方法:static void newman(String x)——判定是否为新用户
void addtable(int n)——判定是否要添加桌
void getttprice()——计算价格
static void show()——打印结果
Ⅲ、为了在程序中调用数据方便,我特意新建了一个数据库的类——Data类。Data类中通过map来存储用户的数据信息,方便后续调用,可以大大减少代码量。个人觉得还是很好用的。
以下是我的该类的代码:
|
class Data static Map<Integer,table>t=new HashMap<>(); |
Ⅳ、类与类之间的关系:详情见如下类图
一些小技巧
Ⅰ、在用户类中利用map存储用户数据信息时,可以通过重载,继承compare接口,实现按照用户名字进行字典序排序功能。
Ⅱ、可以专门建立数据库类,存储所有信息,调用时直接调用该类,可以大大减少参数的传递,梳理逻辑,缩减代码量。
Ⅲ、在定义经常需要调用的属性或者方法时,可以定义成static静态的,这样不需要创建该类的对象,在其他静态类中也能直接使用该属性或者方法。当然、这样肯定也有弊端,这里就不再详细赘述,有兴趣了解的小伙伴,可以看看如下链接的博主发文,讲的非常的细致。
(43条消息) Java的Static关键字的作用_static关键字优先于对象存在吗?_嘻嘻你好呀的博客-CSDN博客
我的Bug:
在编写代码的过程中,最大的问题就是非零返回和空指针异常(即访问到了地址内容为空的元素)。空指针异常在最初接触该题的时候相信是大家都很懊恼的一个问题,空指针异常的报错如下:java.lang.NullPointerException: Cannot read field "******" because "******" is null。看到这个报错,第一反应就是“寄,CPU烧了”。但是在知道这是因为访问到空的元素或者说地址后,就不会再觉得CPU被干爆了。这种错误通常就是因为没有new出相应的类的对象,或者创建了类的数组,但是类的数组元素没有new出来;在使用数组执行数组下标加加时,可能没有理清楚逻辑,数组下标少加了一次,导致空指针报错。这也是我经常容易犯的错误。
非零返回和空指针报错的报错点大致相同,这里就不在重复赘述了。需要注意的是非零返回可能存在很低级很低级的错误,就是在编译器上编写完代码,粘贴到PTA提交时忘记删除“package”,这种细节上的问题还是需要多多注意的,我就在这上面吃了大亏,直接哭死。
好啦,痛苦的菜单系列话题到此结束,下面谈谈占比丝毫不亚于一次大作业的期中考试吧!
期中考试
7-1 测验1-圆类设计:这题没有什么特殊的知识点,知道Java如何使用圆周率——Math.PI即可!
7-2 测验2-类结构设计:本题类图已给出,主要考察根据已知类完成代码的能力,只需要按照类图书写即可。同时略微涉及到构造函数的知 识! 设计一个矩形类,以及点类,矩形由两个点组成(左上,右下),只需要输出面积,唯一麻烦的是所有的属性都 是prevate类型的都要所有对应的set和get方法进行操作,以及要写新的构造方法(在构造时将属性全部设置好)。
|
一点小技巧:计算面积时需要用到长和宽,但是相减时无法确定正负。当然咯,可以使用取绝对值的math.abs函数; 或者直接在最后算出面积时,判定面积正负即可,若为负数,则乘(-1)。 double getArea() |
7-3 测验3-继承与多态:将前两题结合,写一个图形类作为圆和矩形的父类,由于有类图所以并没有什么困难。唯一可能有点坑的就是第一个测试点,谁能想到圆的半径为0是是错误输入呢,可爱蔡轲!
以下展示Main函数以及父类Shape类:
|
import java.util.Scanner; } |
7-4 测验4-抽象类与接口:本题相比于前面没有太大的改变,主要就是需要知道如何使用compare接口;
-
实现Comparable接口
-
接口中的方法为int compareTo(Shape);
- 下面附上源码:
|
// package 期中考试; public class Main { { int choice = input.nextInt(); while(choice != 0) { { list.sort(Comparator.naturalOrder());//正向排序 for(int i = 0; i < list.size(); i++) { }
} class Circle extends Shape class Point double getLength() |
踩坑心得
这里简单总结以下在这三次题集中踩的各种坑的心得和经验吧,有一说一,坑是真坑啊!
1、PTA中编写代码不会实时报错,代码量一多,运行时可能一下报出十几个错,CPU都给看没了,所以建议在eclipse或者idea等编译器上编写。
2、在使用类中的属性或者方法时,务必记得创建对象;同时在创建类的数组,需要使用其元素时也需要对当前元素new一下,否则会报空指针或者非零返回。
3、使用类时一定要new明白,不能重复创建,重复创建会导致当前所使用的类中的数据变为空,出现空指针或者非零返回的问题,切记切记。
4、类与类间是平行的,不能在类中创建类。
5、在类中不能调用属性和方法,只有在方法中才能调用。
6、对于带返回值的感受务必要记得return,否则会非零返回,在编译器上编写代码时如果没有带回返回值·会报错,但是PTA编译却没问题,这时候编译器实时报错的好处又在此体现出来了。
7、在编写代码时遇到的很多问题可能都和圈复杂度较高有关,例如运行超时等。所以编写代码时,可以借用SourceMonitor来查看圈复杂度,尽量控制在8以内(这样代码可读性较高,修改也方便)。
8、还有就是一些细节语法上的错误,要尽量减少这类失误,虽然这个没什么好说的,但是很多问题的出现都是因为一个个细节没有处理好而累积导致的后果。
主要困难以及改进建议
主要困难:我个人认为对于这两次的PTA大作业和期中考试来说,主要的困难不是题目难不难,而是没有花费足够的时间去学习相应的Java的知识。每次大作业发布的时候都需要临时去学习相应的内容,例如对于期中考试中compare接口的基本使用,因为没有学过compare接口排序而不知道如何着手,菜单中重复数据的问题,我知道sort排序、set集合在C++中怎么使用,但是没有学习过在Java中的用法,最大的问题就是菜单系列的类的设计与使用,让我做起来很吃力。所以说,没有平时日积月累的训练学习,到做大作业的时候问题集中起来的时候,就会感觉很吃力,也会影响自己的心情,甚至进而形成恶性循环。
改进建议:当然,这主要还是归咎于个人学习的自觉性,但是外界环境也具备一定的影响。在作业布置中,我认为可以将作业细化一些,发布作业的频率适当提高,作业量适当减少,作业难度也适当降低,当然,时限自然而然也是要减少的。人的潜力和上限是无穷的,在巨大的压力下,才能够爆发出力量。常言道,不逼一逼,怎知自己有多优秀。
总结
一、对Bug的小感悟
bug通常来自三种原因:
1、对题面理解的疏忽
2、对自己代码架构细节出现问题(使用错误方法)
3、粗心导致
4、知识的缺乏
二、学到的东西
1、对正则表达式的使用
2、熟悉理解了类的设计与使用;
3、对面向对象编程(封装性)有了初步的认识;
4、能够自如的使用eclispe编译器;
5、对于各种报错能够理解、从容的去修改;
6、能够熟练对代码进行调试,查找调试过程中代码的各类数据的变化等;
7、当然,我认为最大的收获,还是对与Java这门语言有了一定的理解,能够逐渐窥见面向对象的神秘之处;
8、对于Java的学习还是不能够松懈,不能只跟着老师的进度走,需要将进度提高一些,否则面对大作业就会显得吃力了。
9、最实用的还是利用起来Java提供的各种高级方法,比如Hashmap,HashSet,ArrayList,Vector等等。

浙公网安备 33010602011771号