oop-blog-2
(1) 前言
第四次作业所涉及到的知识点:
字符串处理、正则表达式、集合操作,1. 字符串的比较和匹配:使用equals()方法比较字符串是否相等,使用matches()方法判断字符串是否匹配指定的正则表达式。字符串的分割:使用split()方法将字符串按照指定的分隔符进行分割。字符串转换为整数:使用Integer.parseInt()方法将字符串转换为整数。LocalDateTime类的使用:用于表示日期和时间的类,可以通过of()方法创建指定日期和时间的实例。异常处理:使用try-catch语句捕获DateTimeException异常,并进行相应的处理。ArrayList:records是一个ArrayList对象,用于存储Record对象数学运算和数值处理:代码中使用了一些数学运算符和方法,如四舍五入和乘法运算。
期中考试所涉及到的知识点:
组合关系:Rectangle类包含两个Point对象(topLeftPoint和lowerRightPoint),过组合关系实现矩形的描述。
抽象类和抽象方法:使用abstract关键字定义抽象类Shape和其中的抽象方法getArea(),该方法没有具体的实现逻辑,需要在子类中进行实现。
继承与多态:Circle和Rectangle类继承自抽象类Shape,实现了getArea()方法,并通过多态性将它们存储到Shape类型的变量中。
封装:将圆的半径设置为私有属性,通过公有的方法(setRadius和getArea)来访问和修改属性值,以控制属性的访问权限和操作。
异常处理:在setRadius方法中判断输入的半径是否合法(大于零),如果不合法,则输出错误信息。
数学计算:利用Math.PI常量表示圆周率π,
字符串格式化:使用String.format方法将面积结果保留两位小数,并将其作为字符串输出。
对象比较:将抽象类Shape实现Comparable<Shape>接口,并重写compareTo方法来实现对象之间的比较。排序:使用list.sort(Comparator.naturalOrder())对动态数组进行排序,按照图形面积的大小进行正向排序。
第五次作业所涉及到的知识点:
1.字符串的处理和判断:使用正则表达式判断输入字符串的格式是否符合要求。
2. 异常处理:使用try-catch语句捕获NumberFormatException异常,处理字符串转换为整数时可能出现的错误。
3. 集合的使用:使用ArrayList存储菜单、桌子和用户等对象。
4. 输入输出:使用Scanner类获取用户输入的字符串。
5. 类和对象的使用:定义了Menu、Table和User类,并在主函数中创建了相应的对象。
6. 条件语句和循环语句:使用if-else语句和while循环语句对输入进行判断和处理。
7. 字符串的分割和拼接:使用split方法将输入字符串按空格分割成多个子字符串,并使用加号拼接字符串。
8. 类的成员变量和方法的使用:访问和修改对象的成员变量,调用对象的方法。
9. 面向对象的编程思想:将功能封装成类和方法,提高代码的可读性和可维护性。
整体题目量较小,但是题目难度很大,所涉及到的面很广,很难将所有的东西都有所涵盖。
并且对时间有比较严格的要求,容易造成超时。
(2) 设计与分析
第四次作业和第六次作业设计
根据题目要求,我们需要设计一个点菜计价程序,实现以下功能:
首先,我们可以设计一个菜单类(Menu),包含菜名和基础价格两个属性。可以使用一个列表来存储菜单信息。
然后,我们设计一个订单类(Order),包含桌号标识、点菜记录和删除信息、代点菜信息三个属性。点菜记录和删除信息可以使用一个列表来存储,代点菜信息可以使用一个字典来存储。
接下来,我们可以设计一个计算总价的函数,根据输入的订单信息和菜单信息,计算每桌的总价。在计算总价的过程中,需要考虑折扣和特色菜口味度。可以使用一个字典来存储每桌的总价和口味度。
最后,我们按照桌号从小到大的顺序输出每桌的总价和菜品口味度。可以使用一个循环来遍历每桌的信息,并按照题目要求输出。
在设计程序时,需要注意处理输入错误的情况,比如删除记录时序号不对、口味度超过正常范围等。可以使用条件判断和异常处理来处理这些情况。
第四次作业分析
while (true) {// 输入菜单 Dish temp = new Dish(); int isRepeat = -1; str1 = input.nextLine(); if (str1.matches("[\\S]* [1-9][\\d]*")) { String[] token = str1.split(" "); temp.name = token[0]; temp.unit_price = Integer.parseInt(token[1]); if (temp.unit_price > 300) { System.out.println(temp.name + " price out of range " + temp.unit_price); continue; } temp.isT = false; isRepeat = menu.searchDish(temp.name); if (isRepeat != -1) { menu.dishs.remove(isRepeat); } menu.dishs.add(temp); } else if (str1.matches("[\\S]* [\\d]* T")) { String[] token = str1.split(" "); temp.name = token[0]; temp.unit_price = Integer.parseInt(token[1]); if (temp.unit_price > 300) { System.out.println(temp.name + " price out of range " + temp.unit_price); continue; } temp.isT = true; isRepeat = menu.searchDish(temp.name); if (isRepeat != -1) { menu.dishs.remove(isRepeat); } menu.dishs.add(temp); } else if (str1.equals("end")) { break; } else if (str1.matches("tab.*")) { break; } else { System.out.println("wrong format"); continue; } }
代码从用户输入中读取一行字符串,并将其存储在变量str1中。
如果str1满足正则表达式[\\S]* [1-9][\\d]*,表示输入的格式为"菜名 价格",则继续进行以下处理:
使用空格将str1分割成两个部分,分别存储在数组token中。
创建一个新的Dish对象,并将第一个部分赋值给temp的name属性,将第二个部分转换为整数并赋值给temp的unit_price属性。
如果unit_price大于300,则打印错误信息并继续下一轮循环。
设置temp的isT属性为false。
在menu中搜索是否存在同名的菜品,如果存在,则移除该菜品。
将temp添加到menu的菜品列表中。
如果str1满足正则表达式[\\S]* [\\d]* T,表示输入的格式为"菜名 价格 T",则继续进行以下处理:
与上述步骤相同,但是这次设置temp的isT属性为true。
如果str1等于"end",表示输入的是结束命令,跳出循环。
如果str1满足正则表达式"tab.*",表示以"tab"开头的字符串,跳出循环。
如果以上条件都不满足,则打印错误信息,并继续下一轮循环。
while (!str1.equals("end")) { Table temp = new Table(); boolean isRepeat = false; int repeatNum = 0; if (str1.matches(".*t.*a.*")) { if (str1.matches( "table [1-9][\\d]* [\\d]*/[\\d][\\d]?/[\\d][\\d]? [\\d][\\d]?/[\\d][\\d]?/[\\d][\\d]?")) { String[] token = str1.split(" "); String[] Date = token[2].split("/"); String[] Time = token[3].split("/"); int[] intDate = new int[3]; int[] intTime = new int[3]; for (i = 0; i < 3; i++) { intDate[i] = Integer.parseInt(Date[i]); intTime[i] = Integer.parseInt(Time[i]); } temp.num = Integer.parseInt(token[1]); if (temp.num > 55) { System.out.println(temp.num + " table num out of range"); str1 = input.nextLine(); continue; } try { temp.time = LocalDateTime.of(intDate[0], intDate[1], intDate[2], intTime[0], intTime[1], intTime[2]); temp.getWeekDay(); } catch (DateTimeException e) { System.out.println(temp.num + " date error"); str1 = input.nextLine(); continue; } if (!temp.isOpen()) { System.out.println("table " + temp.num + " out of opening hours"); str1 = input.nextLine(); continue; } if (!(temp.time.isAfter(LocalDateTime.of(2022, 1, 1, 0, 0, 0)) && temp.time.isBefore(LocalDateTime.of(2024, 1, 1, 0, 0, 0)))) { System.out.println("not a valid time period"); str1 = input.nextLine(); continue; } // 判断桌号是否重复 if (temp.isOpen()) { for (i = 0; i < tables.size(); i++) { // 有重复的桌号 if (temp.num == tables.get(i).num && tables.get(i).isOpen()) { Duration duration = Duration.between(temp.time, tables.get(i).time); // 同一天 if (duration.toDays() == 0) { // 在周一到周五 if (temp.weekday > 0 && temp.weekday < 6) { // 在同一时间段 if (temp.time.getHour() < 15 && tables.get(i).time.getHour() < 15) { isRepeat = true; repeatNum = i; break; } } // 在周末 else { // 时间相差小于一小时 if (duration.toHours() < 3600) { repeatNum = i; isRepeat = true; break; } } } } } } System.out.println("table " + temp.num + ": "); } else { System.out.println("wrong format"); str1 = input.nextLine(); continue; }
当输入的字符串不等于"end"时执行循环体内的代码。循环体内首先创建了一个Table对象temp,并将isRepeat标志设为false,repeatNum设为0。
接下来,通过正则表达式判断输入的字符串是否匹配特定的格式。如果匹配成功,则将该字符串按空格进行分割,并提取出日期和时间信息。将提取到的日期和时间信息转换为整型并存储在intDate和intTime数组中。
然后,将字符串中的桌号部分转换为整型并赋值给temp的num属性。若num大于55,则输出桌号超出范围的提示信息,并进入下一次循环。
接着,使用Java的LocalDateTime类将日期和时间信息组合成一个完整的时间对象,并判断该对象是否有效。若无效,则输出日期错误的提示信息,并进入下一次循环。
之后,判断temp所表示的时间是否在开放时间范围内,若不在范围内,则输出桌子不在开放时间内的提示信息,并进入下一次循环。
再接下来,通过比较temp与已有桌子的信息,判断桌号是否重复。如果发现重复的桌号且在同一天,根据条件判断是否为重复桌子,并记录重复桌子的索引。
最后,输出当前桌子的信息。
如果输入的字符串不符合特定格式,则输出格式错误的提示信息,并进入下一次循环。
期中作业分析
7-1 测验1-圆类设计
就是判断数据的非法性额结果的利用String.Format标准化输出
7-2 测验2-类结构设计
要建立矩形和点类,组合成聚合关系,将其成员进行封装,在矩形类中调用点类,实现结果,通过一下三个方法实现面积的计算。
7-3 测验3-继承与多态
在主函数中实现switch,其中相对应不同的形状,输入不同的数据,计算出不同的面积,其中circle和Rectangle都继承于父类shape,实现多态
7-4测验4-抽象类与接口
通过列表ArrayList<Shape> list = new ArrayList<>();,现将输入的数据存入相应的对象,然后在列表中添加,最后通过循环遍历
,
实现接口
第五次作业分析
for (i = 0; i < tables.size(); i++) { if (tables.get(i).isOpen()) { tables.get(i).getSum(); tables.get(i).getFlavorNum(); if(i!=0)System.out.print("\n"); tables.get(i).showEnd(); boolean isRepeat = false; for(int j=0;j<users.size();j++) { if(tables.get(i).userName.equals(users.get(j).userName)) { users.get(j).paidMoney+=tables.get(i).sum; isRepeat = true; break; } } if(!isRepeat) { User tempUser = new User(); tempUser.userName = tables.get(i).userName; tempUser.callNum = tables.get(i).callNum; tempUser.paidMoney = tables.get(i).sum; users.add(tempUser); } } } users.sort(new Comparator<User>() { public int compare(User a,User b) { return a.userName.compareTo(b.userName); } }); for(i=0;i<users.size();i++) { System.out.print("\n"+users.get(i).userName +" "+ users.get(i).callNum +" "+ users.get(i).paidMoney); } }
首先,使用循环遍历tables列表中的每个元素。如果当前桌子是开放状态(isOpen()为true),则执行以下操作:
调用getSum()方法获取当前桌子的总金额。
调用getFlavorNum()方法计算当前桌子上川菜、晋菜和浙菜的数量和口味类型。
使用条件判断语句if(i!=0)控制换行输出。
调用showEnd()方法输出当前桌子的相关结果。
遍历users列表,查找是否存在与当前桌子的用户名相同的用户记录。
如果存在,则将当前桌子的总金额加到该用户的已支付金额上。
如果不存在,则创建一个新的用户记录,并将当前桌子的用户名、呼叫次数和总金额赋值给该用户记录,然后添加到users列表中。
接下来,使用Comparator对users列表进行排序,按照用户名的字母顺序进行排序。
最后,再次使用循环遍历users列表,使用System.out.print语句输出每个用户的用户名、呼叫次数和已支付金额
void getFlavorNum() { for(int i=0;i<records.size();i++) { if(records.get(i).d.isT&&!records.get(i).isDeleted&&!records.get(i).isTreat) { if(records.get(i).d.FlavorType.equals("spicy")) { ChuanNum+=records.get(i).quota; spicyType +=records.get(i).FlavorNum*records.get(i).quota; } if(records.get(i).d.FlavorType.equals("acidity")) { JinNum+=records.get(i).quota; acidType +=records.get(i).FlavorNum*records.get(i).quota; } if(records.get(i).d.FlavorType.equals("sweetness")) { ZheNum+=records.get(i).quota; sweetType +=records.get(i).FlavorNum*records.get(i).quota; } } } if(ChuanNum!=0)spicyType = Math.round(spicyType/ChuanNum*1.0); if(JinNum!=0)acidType = Math.round(acidType/JinNum*1.0); if(ZheNum!=0)sweetType = Math.round(sweetType/ZheNum*1.0); } void showEnd() { System.out.print("table " + num + ": " + origSum + " " + sum); //输出川菜 if(ChuanNum!=0) { System.out.print(" 川菜 "+ChuanNum); switch((int)spicyType) { case 0 :System.out.print(" 不辣");break; case 1 :System.out.print(" 微辣");break; case 2 :System.out.print(" 稍辣");break; case 3 :System.out.print(" 辣");break; case 4 :System.out.print(" 很辣");break; case 5 :System.out.print(" 爆辣");break; } } //输出晋菜 if(JinNum!=0) { System.out.print(" 晋菜 "+JinNum); switch((int)acidType) { case 0 :System.out.print(" 不酸");break; case 1 :System.out.print(" 微酸");break; case 2 :System.out.print(" 稍酸");break; case 3 :System.out.print(" 酸");break; case 4 :System.out.print(" 很酸");break; } } //输出浙菜 if(ZheNum!=0) { System.out.print(" 浙菜 "+ZheNum); switch((int)sweetType) { case 0 :System.out.print(" 不甜");break; case 1 :System.out.print(" 微甜");break; case 2 :System.out.print(" 稍甜");break; case 3 :System.out.print(" 甜");break; } } }
getFlavorNum()方法通过遍历记录(records)列表,计算出川菜、晋菜和浙菜的数量,并根据具体口味类型进行计算。其中:
如果记录(records)中的菜品类型(d.FlavorType)为"spicy"(辣味),则累加川菜数量(ChuanNum)以及辣味类型菜品的数量乘以配额(FlavorNum*quota);
如果记录中的菜品类型为"acidity"(酸味),则累加晋菜数量(JinNum)以及酸味类型菜品的数量乘以配额;
如果记录中的菜品类型为"sweetness"(甜味),则累加浙菜数量(ZheNum)以及甜味类型菜品的数量乘以配额。
然后,根据实际情况,对辣味(spicyType)、酸味(acidType)和甜味(sweetType)进行计算,并保留整数部分。
showEnd()方法用于输出相关结果。它会输出表号(num)、原始总数量(origSum)、当前总数量(sum)等信息,并根据川菜数量和口味类型、晋菜数量和口味类型、浙菜数量和口味类型进行相应的输出。
while (!str1.equals("end")) { Table temp = new Table(); boolean isRepeat = false; int repeatNum = 0; if (str1.matches(".*t.*a.*")) { if (str1.matches( "table [1-9][\\d]* : [\\S]* [\\d]* [\\d]*/[\\d][\\d]?/[\\d][\\d]? [\\d][\\d]?/[\\d][\\d]?/[\\d][\\d]?")) { String[] token = str1.split(" "); temp.userName = token[3]; if(token[3].length()>10) { str1 = input.nextLine(); System.out.println("wrong format"); continue; } temp.callNum = token[4]; if(token[4].length()!=11) { str1 = input.nextLine(); System.out.print("wrong format"); continue; } String headNum = token[4].substring(0, 3); if(!headNum.equals("180")&&!headNum.equals("181")&&!headNum.equals("189")&&!headNum.equals("133")&&!headNum.equals("135")&&!headNum.equals("136")) { str1 = input.nextLine(); System.out.println("wrong format"); continue; } String[] Date = token[5].split("/"); String[] Time = token[6].split("/"); int[] intDate = new int[5]; int[] intTime = new int[6]; for (i = 0; i < 3; i++) { intDate[i] = Integer.parseInt(Date[i]); intTime[i] = Integer.parseInt(Time[i]); } temp.num = Integer.parseInt(token[1]); try { temp.time = LocalDateTime.of(intDate[0], intDate[1], intDate[2], intTime[0], intTime[1], intTime[2]); temp.getWeekDay(); } catch (DateTimeException e) { System.out.println(temp.num + " date error"); str1 = input.nextLine(); continue; } if (!temp.isOpen()) { System.out.println("table " + temp.num + " out of opening hours"); str1 = input.nextLine(); continue; } System.out.println("table " + temp.num + ": "); } else { System.out.println("wrong format"); str1 = input.nextLine(); continue; }
首先,判断str1是否匹配了包含字母"t"和"a"的字符串。如果满足条件,则进一步判断是否匹配了指定的格式。如果匹配成功,则将str1按空格分割成若干个子字符串,并依次赋值给相应的变量。
接下来,对获取到的各个变量进行合法性校验。例如,检查用户名长度、手机号码格式、手机号码前缀以及日期时间格式等。如果有任何合法性校验不通过,会输出错误信息,并继续下一次循环。
然后,判断当前桌子是否在营业时间内。如果不在营业时间内,会输出错误信息,并继续下一次循环。
最后,输出当前桌子的相关信息。
无论循环是否继续,都会读取下一行输入赋值给str1,用于下一次循环判断。
(3) 踩坑心得
在上述代码实现过程中
在使用正则表达式进行字符串匹配时,无法正确匹配到目标字符串,导致一些代码无法正常编译。
输入的格式不符合预期的格式要求,用户名长度超过限制、手机号码格式错误或日期时间格式错误等,导致后续的校验失败或出现异常。
在提取和处理分割后的子字符串时,索引超出数组的范围,导致数组越界错误。
在构建LocalDateTime对象时,输入的年、月、日、时、分、秒等数值无法构成一个有效的日期时间,抛出DateTimeException异常。
上述错误都很让人头疼。
(4) 改进建议
需要增加错误处理机制:在用户输入不符合预期格式时给出明确的错误信息。可以考虑使用异常处理语句(try-catch语句)来捕获和处理异常情况。
可以提取一些重复的逻辑为方法或函数,以提高代码的可读性和可维护性。例如,可以将验证菜品是否存在、验证数量范围、验证桌号是否存在等逻辑提取成单独的方法。
使用合适的命名方式:变量和方法名最好能够准确地描述其用途和含义,提高代码的可读性。
考虑使用更通用的正则表达式来匹配用户输入,以满足更宽泛的格式要求。
(5) 总结
在完成本阶段的三次题目集后,我深刻地认识到了编程的重要性和必要性。首先要掌握最简单if,else,循环的使用,了解一些特殊类的相关方法的使用。通过这些题目的编程实践,我不仅掌握了基本的编程语法和算法,也提升了自己的逻辑思维和解决问题的能力。同时,我也发现了自己在编程过程中的不足之处,需要进一步学习和研究。
首先,我认为需要进一步学习和研究的是算法和数据结构方面的知识。在完成题目集的过程中,我发现自己对于一些复杂的算法和数据结构的理解还不够深入,需要加强学习和练习。其次,我也需要更加熟练地掌握编程语言的使用,特别是一些常用的库和框架的使用。
对于教师、课程、作业、实验、课上及课下组织方式等方面的改进建议及意见,我认为可以从以下几个方面进行改进。首先,可以增加一些实际应用场景的案例,帮助学生更好地理解和掌握编程知识。其次,可以加强编程实践的环节,让学生更多地进行实际的编程练习。最后,可以增加一些与编程相关的讨论和交流活动,促进学生之间的交流和合作