(1).前言:
大一下学期开始,我们迎来了学习Java这门编程语言的课程。Java被特意设计为适用于互联网和分布式环境的编程语言,它具备了许多其他编程语言的共同特征。与C++相比,在形式和感觉上,Java与之类似,但更易于使用,并且完全采用了以对象为导向的编程方式。
在Pta作业中,我们已经完成了五次作业。尽管第一次作业的题目相对较简单,但随后的作业难度激增。我只在第一次作业中能够完整地完成所有的pta题目,之后再也没有拿到满分。总体来说,最近两次pta作业的主要知识点仍然是设计类和学习Java语言,并应用面向对象的思维方式。对于我们这些只接触过一点Java语言的同学来说,确实是一个挑战。最近两次作业的题目类型虽然相似,但难度非常高,涉及到菜单的设计、多个类的调用等,非常复杂,还需要使用许多函数,并且需要理解继承和传参的概念。对我们来说,确实是十分困难的。
因此,我们需要努力提升自己的编程能力,更加深入地理解Java语言的特性和面向对象的编程思想。
(2) 设计与分析:
1.第四次PTA:
在此次PTA作业中,在我看来其实是上一次PTA作业的拓展与延伸,
本次作业比菜单计价系列-3增加的功能:
菜单输入时增加特色菜,特色菜的输入格式:菜品名+英文空格+基础价格+"T"
例如:麻婆豆腐 9 T
菜价的计算方法:
周一至周五 7折, 周末全价。
实验类图:

在这次实验中,也同样用到了正则表达式进行数据的处理:
public static boolean judgeDate(String nyr,String sfm){ if (nyr.split("/")[0].length() != 4) {return true;} if (nyr.split("/")[1].length() > 2 || nyr.split("/")[1].length() < 1) {return true;} if (nyr.split("/")[2].length() > 2 || nyr.split("/")[2].length() < 1) {return true;} if (sfm.split("/")[0].length() > 2 || sfm.split("/")[0].length() < 1) {return true;} if (sfm.split("/")[1].length() > 2 || sfm.split("/")[1].length() < 1) {return true;} return sfm.split("/")[2].length() > 2 || sfm.split("/")[2].length() < 1; }
这段代码是一个用于判断日期格式是否合法的方法。该方法接受两个参数:nyr表示年月日,sfm表示时分秒。
-
if (nyr.split("/")[0].length() != 4) {return true;}: 这行代码首先使用split("/")将nyr字符串按照"/"拆分成字符串数组。然后通过[0]获取数组中的第一个元素,也就是年份部分。最后使用length()方法获取年份字符串的长度。如果年份长度不等于4,则返回true,表示年份不符合要求。 -
if (nyr.split("/")[1].length() > 2 || nyr.split("/")[1].length() < 1) {return true;}: 这行代码与上一行类似,只是检查的是拆分后数组的第二个元素,即月份部分。如果月份长度大于2或小于1,则返回true,表示月份不符合要求。 -
if (nyr.split("/")[2].length() > 2 || nyr.split("/")[2].length() < 1) {return true;}: 这行代码检查的是拆分后数组的第三个元素,即日期部分。如果日期长度大于2或小于1,则返回true,表示日期不符合要求。 -
if (sfm.split("/")[0].length() > 2 || sfm.split("/")[0].length() < 1) {return true;}: 这行代码检查的是拆分后数组的第一个元素,即小时部分。如果小时长度大于2或小于1,则返回true,表示小时不符合要求。 -
if (sfm.split("/")[1].length() > 2 || sfm.split("/")[1].length() < 1) {return true;}: 这行代码与上一行类似,只是检查的是拆分后数组的第二个元素,即分钟部分。如果分钟长度大于2或小于1,则返回true,表示分钟不符合要求。 -
return sfm.split("/")[2].length() > 2 || sfm.split("/")[2].length() < 1;: 这行代码检查的是拆分后数组的第三个元素,即秒钟部分。如果秒钟长度大于2或小于1,则返回true,表示秒钟不符合要求。
如果以上任何一个判断条件为true,则说明日期或时间格式不合法,该方法将返回true,否则返回false表示日期格式合法。
在此次作业中,为了处理各种异常,我选择选择运用try和catch进行处理异常,例如:
try { date1 = sdf1.parse("2022/1/1"); date2 = sdf1.parse("2023/12/31"); if (date1.compareTo(sdf1.parse(strings[2])) > 0 || date2.compareTo(sdf1.parse(strings[2])) < 0) { List.add("not a valid time period"); continue; } } catch (ParseException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }
这段代码是一个异常处理块,其目的是解析日期字符串并比较日期范围的有效性。让我来逐行解释它的功能:
-
date1和date2是预先定义的两个日期变量。 -
sdf1是一个SimpleDateFormat对象,用于定义日期解析格式。 -
sdf1.parse("2022/1/1")将字符串"2022/1/1"解析成日期,并将结果赋值给date1变量。 -
sdf1.parse("2023/12/31")将字符串"2023/12/31"解析成日期,并将结果赋值给date2变量。 -
date1.compareTo(sdf1.parse(strings[2]))通过调用compareTo()方法比较date1和第三个strings数组元素解析后的日期的大小关系。 -
如果
date1大于第三个数组元素解析后的日期或者date2小于第三个数组元素解析后的日期,则进入if语句块。 -
在if语句块中,向一个名为
List的列表中添加了一个字符串"not a valid time period",表示该时间段无效。 -
continue语句用于跳过当前循环的剩余部分,继续下一次循环(如果有的话)。 -
ParseException是一个异常类,当日期解析过程中出现错误时,会抛出该异常。 -
e1.printStackTrace()用于打印异常的堆栈信息,方便调试代码。
总结来说,该段代码的作用是解析日期字符串并检查它们是否在指定的时间范围内。如果不在范围内,则将错误信息添加到列表中,并继续处理下一个日期字符串。如果解析过程中出现异常,则会打印异常信息。
在其他位置的改动就是一些常规的改动,在我看来并没有什么说明的必要了。
2.第五次PTA
此次PTA作业并不是在点菜4上接着进行改动,而是成了一个分支,与点菜四一样,也是在点菜三上进行的修改
本次课题相比菜单计价系列-3新增要求如下:
1、菜单输入时增加特色菜,特色菜的输入格式:菜品名+英文空格+口味类型+英文空格+基础价格+"T"
例如:麻婆豆腐 川菜 9 T
菜价的计算方法:
周一至周五 7折, 周末全价。
特色菜的口味类型:川菜、晋菜、浙菜
2、考虑客户订多桌菜的情况,输入时桌号时,增加用户的信息:
格式:table+英文空格+桌号+英文空格+":"+英文空格+客户姓名+英文空格+手机号+日期(格式:YYYY/MM/DD)+英文空格+ 时间(24小时制格式: HH/MM/SS)
实验类图:

而对于此次实验,我对如何进行菜品的类型该如何进行判断进行了思考,后面我给出以下的解决方式:
for (Record record : recordss) { if (record != null) { if (record.getReplace().getOriginNum() - 1 == count) { if ("川菜".equals(record.getDish().getType())) { n1--; nn1 -= record.getNum(); sumTaste1 -= record.getSpicy() * record.getNum(); } if ("晋菜".equals(record.getDish().getType())) { n2--; nn2 -= record.getNum(); sumTaste2 -= record.getSpicy() * record.getNum(); } if ("浙菜".equals(record.getDish().getType())) { n3--; nn3 -= record.getNum(); sumTaste3 -= record.getSpicy() * record.getNum(); } } if (record.getReplace().getReplaceNum() - 1 == count) { if ("川菜".equals(record.getDish().getType())) { n1++; nn1 += record.getNum(); sumTaste1 += record.getSpicy() * record.getNum(); } if ("晋菜".equals(record.getDish().getType())) { n2++; nn2 += record.getNum(); sumTaste2 += record.getSpicy() * record.getNum(); } if ("浙菜".equals(record.getDish().getType())) { n3++; nn3 += record.getNum(); sumTaste3 += record.getSpicy() * record.getNum(); } } } else { break; } }
这段代码是针对一个名为recordss的记录列表进行遍历操作。在每次循环中,首先会检查当前遍历到的记录record是否为空,如果不为空则执行以下逻辑:
-
首先,判断
record的Replace对象中的OriginNum属性减去1是否等于count。如果满足条件,进一步判断record所代表的菜品类型是否为"川菜"、"晋菜"或"浙菜"。如果是其中之一,执行相应的操作:将对应类型菜品的计数变量递减1,减去该记录的数量,并减去该记录的辣度值乘以数量后的结果。 -
接着,判断
record的Replace对象中的ReplaceNum属性减去1是否等于count。如果满足条件,同样进一步判断record所代表的菜品类型是否为"川菜"、"晋菜"或"浙菜"。如果是其中之一,执行相应的操作:将对应类型菜品的计数变量递增1,加上该记录的数量,并加上该记录的辣度值乘以数量后的结果。 -
如果
record为空,即遍历到了一个空记录,则跳出循环。
上述代码主要是对记录列表中的记录进行操作,根据条件对不同类型的菜品进行计数、数量总和以及辣度总和的调整。
并且我认为时间的处理方式也有一定的借鉴作用:
public static int dateToWeek(String dateTime) { SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd"); int[] weekDays = new int[]{7, 1, 2, 3, 4, 5, 6}; Calendar calendar = Calendar.getInstance(); // 获得一个日历 Date date = null; try { // 将String类型数据转换成Date类型 date = f.parse(dateTime); calendar.setTime(date); } catch (ParseException e) { e.printStackTrace(); } //指示一个星期中的某天 int w = calendar.get(Calendar.DAY_OF_WEEK) - 1; if (w < 0) { w = 0; } return weekDays[w]; }
这段代码是一个静态方法 dateToWeek,接受一个字符串类型参数 dateTime,用于将日期转换为对应的星期几。
首先,代码创建了一个 SimpleDateFormat 对象 f,用于定义日期字符串的格式为 "yyyy/MM/dd"。然后,定义了一个整型数组 weekDays,存储了从星期日到星期六对应的数字,用于将 Calendar.DAY_OF_WEEK 返回的数字转换为对应的星期几。
接下来,代码创建了一个 Calendar 实例 calendar,并通过 getInstance 方法获取当前日期和时间的 Calendar 对象。然后,通过 SimpleDateFormat 的 parse 方法将传入的日期字符串 dateTime 解析为 Date 对象,并将其设置给 Calendar 实例 calendar。
最后,通过 calendar.get(Calendar.DAY_OF_WEEK) 获取当前日期对应的星期数,并根据 weekDays 数组将其转换为对应的数字,即星期几。如果结果小于 0,则将其置为 0,并作为函数的返回值。
需要注意的是,该方法在使用过程中需要确保传入的日期字符串格式与代码中定义的格式 "yyyy/MM/dd" 一致,否则可能会出现解析错误。此外,代码中的异常处理部分只是简单地打印了异常堆栈信息,可以根据实际需求进行适当修改。
其余也就是对代码进行常规的修改与改进
3.期中考试:
在我看来,期中考试的大多数题目其实没什么好说的,因为前三题我觉得很简单,完全就是按照题目所说的对类进行构建然后就可以获得满分,但是最后一题的一些细节还有有点意思的,在最后一题中,我们需要对一个对象进行判断,判断其到底来自与哪个接口的处理,下面是我思考后的处理方式:
static void printArea(Shape shape) { double f = shape.getArea(); if (shape instanceof Circle) { if (f <= 0) System.out.print("Wrong Format"); else { String tmp = String.format("%.2f", f); System.out.print(tmp); } } else { String tmp = String.format("%.2f", f); System.out.print(tmp); } }
这是一个名为printArea的静态方法,接受一个Shape类型的参数shape。该方法用于计算并打印出给定形状的面积。
首先,方法中声明了一个双精度类型的变量f,用于存储调用shape对象的getArea方法所返回的面积值。
接下来使用了一个条件语句if,判断shape对象是否属于Circle类。如果属于Circle类,则进入if块。在if块中,首先检查面积值f是否小于等于0,如果是,则打印出"Wrong Format",表示格式错误。如果面积值大于0,则使用String.format("%.2f", f)将面积值格式化为保留两位小数的字符串tmp,并将tmp打印出来。
如果shape对象不属于Circle类,那么直接进入else块。在else块中,同样使用String.format("%.2f", f)将面积值格式化为保留两位小数的字符串tmp,并将tmp打印出来。
因此,该方法根据给定形状的类型和面积值进行不同的处理,并将结果打印出来。
其他我觉得没有什么好说的。
(3) 采坑心得:
1、注意检查价格算法,特别是特色菜的定价算法和打折时间。确保算法正确并准确反映实际情况。
2、当点菜记录中存在相同菜名和份额的情况时,应该将它们合并成一条记录进行计算,以避免四舍五入误差带来的问题。
3、请注意处理超出范围的返回值错误,避免返回不正确的数据或者返回无效的日期。
4、在调用方法类函数时,请确保在方法名后加上括号()来进行调用,以避免报错。
5、在使用Java语言时,要注意大小写。Java对于输入的语法要求非常严格,很多报错是因为方法名称没有正确的大写首字母。
6、记得在编写代码时,所有的大括号“{}”都需要成对出现。有时候我们只记得输入左括号“{”,却忘记输入与之对应的右括号“}”。
(4)改进建议:
1.在程序的编写中,还是要多运用list之类的数据存储方式,数组之类的方式太过于麻烦。
2.在使用继承的方法时,可能还不够熟练导致出现一些错误。同样,在传递参数的方法方面也缺乏一些熟练度,容易出现错误。为了避免未来出现错误,建议多加练习和使用这些特性,以增加对它们的熟悉程度。只有通过实践和经验积累,才能更好地掌握和运用这些技巧。
3.在进行类的设计时,确实需要更加细节和具体。每个类应该明确定义其包含的属性和方法,以及不同类之间的关系。
-
属性:确定每个类所需的属性,这些属性代表类的特征和状态。例如,餐厅类可能包括名称、地址、联系方式等属性;菜品类可能包括名称、价格、描述等属性。
-
方法:确定每个类应该具备的方法,这些方法用于执行相应的操作。例如,在餐厅类中可能包括预订、点菜、结账等方法;在菜品类中可能包括展示菜品、计算总价等方法。
-
类之间的关系:考虑不同类之间的关系,例如继承、关联或依赖关系等。例如,可以创建一个订单类,它关联了餐厅类和菜品类,表示顾客在某个餐厅点的菜品。
在设计类时,需要深入思考每个类的作用、责任和关联,确保类的设计合理,并且能够满足系统的需求。同时,注意遵循面向对象设计原则,如单一职责原则、开闭原则等,以提高代码的可维护性和灵活性。
(5) 总结:
在七到十周的学习过程中,我通过实验、考试和作业进一步巩固了课堂上学到的知识。这些包括类的声明、创建与使用方法、类的构造方法的定义与使用方法、引用变量与对象实例之间的关系与区别以及方法重载的实现方式等等。通过这些练习,我填补了在上课时没有完全理解或者没有听懂的知识点,比如容器类在期中考试的第三题中涉及到的内容。然而,在构造一个对象数组的构造上,我还有一些困惑。另外,我还没有详细了解抽象父类中方法是否需要用抽象方法或空方法体的差别,但我打算花时间进一步了解这方面的知识。