OOP三次大作业总结-BLOG-1
面向对象程序设计三次大作业总结
一、前言。
第一次作业共有九道题目,都是考察Java的基础语法,我认为是用来让我们适应从C语言到Java的转变的(虽然我没做全对),没有运用上Java特色:类与对象;
第二次作业共四道题,随着学习进度的前进,我们也迎来了对象和类的学习,第二次作业用上了对象和类,还有方法,相对难度大大增加。其中,7-1和7-2的菜单计价程序,渗透到了面向对象的过程,类和对象的使用以及方法的构造等。主要难点在于:1.类的设计与使用:要根据题目要求设计出合适的类,每个类具有不同的属性、方法及构造方法,在程序中实例化这些类并调用其方法实现功能;2.字符串处理:输入的菜名和份额信息需要通过字符串处理来提取和解析数据,以便后续的计算和输出;3.控制流语句:程序需要正确地选择和执行分支语句、循环语句和跳转语句,以实现正确的用户交互和求解过程。7-4的小明走格子,我怎么都过不了运行超时的坎,后来通过查阅资料,发现可以用字符流输入(即一次只输入一个字符)实现该题,可以通过调用Scanner类的next()方法从控制台读取下一个单词,并将该字符串转化为一个字符数组char[] input。然后将该字符数组作为参数传递给countWays方法,开始递归计算从起点到终点的所有走法总数。
第三次作业共七道题,第一题菜单我没写出来(冷汗冷汗),7-3去掉重复的数字运用了输入缓冲流方法,使用Java中的Set集合来实现去重,以及使用StringBuilder来进行字符串的拼接。Set是Java集合框架中的一种数据结构,它是一个不包含重复元素的集合,需要注意的是,当元素存在重复时,Set会自动去重,因此最终的结果不会包含任何重复元素。这种去重方法也比较高效,尤其适用于对大量数据进行去重的情况。7-5面向对象编程(封装性)则主要是对封装性的理解(说实话挺难理解的)。
二、设计与分析。
三道菜单计价程序问题的设计与分析:
1.菜单计价程序-1
1)类图:
2)题目:
某饭店提供4种菜,每种菜品的基础价格如下:
西红柿炒蛋 15
清炒土豆丝 12
麻婆豆腐 12
油淋生菜 9
设计点菜计价程序,根据输入的订单,计算并输出总价格。
订单由一条或多条点菜记录组成,每条记录一行,最后以"end"结束
每条点菜记录包含:菜名、份额两个信息。
份额可选项包括:1、2、3,分别代表小、中、大份)
不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格2。
如果计算出现小数,按四舍五入的规则进行处理。
3)分析:
Dish类:表示一个餐品,包括名字和单价等属性。其中 getPrice(int portion) 方法用于根据不同的食物数量计算对应的价格。
Menu类:表示菜单类,包括添加餐品,以及通过名称查找餐品等方法。
Record类:表示一条记录,即某个餐品和对应的数量组成的记录。其中 getPrice() 方法调用了 Dish 的 getPrice(int portion) 方法计算当前记录的价格。
Order类:表示订单类,包含记录数组,可以根据输入的餐品名称、数量以及菜单信息创建新的 Record 对象,并将其加入到记录数组中。同时,还提供了计算订单总价的 getTotalPrice() 方法。
2.菜单计价程序-2
1)类图:
2)题目:
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"end"结束。
菜单由一条或多条菜品记录组成,每条记录一行
每条菜品记录包含:菜名、基础价格 两个信息。
订单分:点菜记录和删除信息。每一类信息都可包含一条或多条记录,每条记录一行。
点菜记录包含:序号、菜名、份额、份数。
份额可选项包括:1、2、3,分别代表小、中、大份。
删除记录格式:序号 delete
标识删除对应序号的那条点菜记录。
不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格2。
如果计算出现小数,按四舍五入的规则进行处理。
3)分析:
Menu 类:此类负责管理菜单项,其中 addDish 方法可用于将新的餐品(具有名称和价格)添加到菜单列表中。另外, findDishByName 方法可用于查找指定名称的餐品。
Dish 类: 表示某一道菜品,包含了名称、单价等基本属性及操作方法。
Record 类:表示购物车中的一条记录,即某个餐品和对应数量组成的记录。其中 price 方法用于计算当前条目的总价格。
Order 类:表示订单,可以通过菜单和记录列表来创建一个 Order 对象。addARecord 和 delARecordByOrderNum 两个方法可用于添加或删除错误的记录。getTotalPrice 方法可用于获取整个订单的总价值。
3.菜单计价程序-3
1)类图:
2)题目:
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"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"
3)分析:
代码的主要功能是管理餐厅点餐、结账以及生成报表。程序在输入过程中通过 Scanner 类来获取控制台上的输入信息,处理完成后将结果输出到控制台。
具体来说,该程序包含一个 Dish 类和一个 Table 类,Dish 类用于存储菜品名称和价格,而 Table 类用于存储桌号、日期、时间和订单信息等。程序主函数采用 while 循环来不断读取输入,根据用户输入执行相应的操作,直到用户输入 end 或 table 命令结束程序。
程序主体部分主要实现了以下功能:
-
解析输入并存储菜品信息:整个程序最开始先解析用户输入得到菜名及其对应的价格,并按输入顺序存储到 dishes 数组中。
-
获取输入中的桌号、日期、时间等信息:程序从输入中获取桌号、日期、时间等信息,并存储在 tables 数组中。
-
处理订单:程序读取用户输入中的“订单编号-菜品名称-份数-数量”信息,并按照特定的格式存储在 order 数组中,同时计算每个订单的总价并打印在控制台上。
-
结账:当用户输入 end 命令时,程序先遍历所有 tables,统计其中的所有订单价格总和,并输出结账信息。
三、采坑心得。
1.非法输入:
第一次作业7-9二进制数值提取:
import java.util.*; import java.lang.*; public class Main{ public static void main(String[] args){ Scanner input = new Scanner(System.in); String str=input.nextLine(); int Len=str.length(); int Flag=0; for(int t=0;t<Len;t++) { if(str.charAt(t)=='-'&&str.charAt(t+1)=='1'){ Flag=1; break; } if(str.charAt(t)=='0'||str.charAt(t)=='1'){ System.out.print(str.charAt(t)); } } if(Flag==0){ System.out.println("Wrong Format"); } } }
这段代码存在问题是:对于长度为1的字符串输入时,访问了后面一个字符。
当输入长度仅为1的字符串时,在循环中判断if(str.charAt(t)=='-'&&str.charAt(t+1)=='1')会访问到字符串外面的内存空间,会产生StringIndexOutOfBoundsException异常。可以添加if(Len==1) return;来避免这个问题。
2.运行超时:
第二次作业7-4小明走格子:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int m = scanner.nextInt(); while (m-- > 0) { int n = scanner.nextInt(); int[] dp = new int[n + 1]; dp[0] = 1; dp[1] = 1; if (n >= 2) { dp[2] = 2; } if (n >= 3) { dp[3] = 4; } for (int i = 4; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] + dp[i - 4]; } System.out.println(dp[n]); } } }
在网上查阅的资料建议可以通过字符流输入(BufferedReader),来提升输入速度,从而减少运行时间;
还有的建议是使用滚动数组。因为dp[i]只与dp[i-1]、dp[i-2]、dp[i-3]、dp[i-4]有关,所以可以将数组中的元素不断往后移,并使用一个指针表示当前计算到的位置。这样可以避免不必要的空间浪费,从而降低时间复杂度。
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int m = scanner.nextInt(); while (m-- > 0) { int n = scanner.nextInt(); int[] dp = new int[5]; dp[0] = 1; dp[1] = 1; if (n >= 2) { dp[2] = 2; } if (n >= 3) { dp[3] = 4; } int cur = 4; while(cur <= n){ int next = dp[3] + dp[2] + dp[1] + dp[0]; dp[0] = dp[1]; dp[1] = dp[2]; dp[2] = dp[3]; dp[3] = next; cur++; } System.out.println(dp[3]); } } }
使用滚动数组后,只需要记录数组中最后四个元素,并随着计算不断往后更新,即可得到dp[n]的值。因此,在计算dp[i]时,只需要进行一次加法运算,而不需要再次访问前面的数组元素,从而降低了时间复杂度,避免了超时的问题。
四、主要困难以及改进建议。
主要困难:
1.菜单计价程序需要维护菜品的名称、价格、份数、总价等信息,需要建立相应的数据模型来存储和管理这些信息。如果数据模型设计不合理,会影响程序的可维护性、扩展性和性能。
2.在计算总价时,可能会出现精度丢失或者舍入误差等问题,导致计算结果与实际结果存在差异。为了避免这个问题,程序需要使用适当的数据类型和算法,并进行精度控制和错误处理。
3.如果菜单计价程序需要支持多用户并发操作,需要考虑多线程并发访问带来的数据一致性和安全性问题,并使用适当的同步机制进行控制。
4.菜单计价程序可能涉及到多个相似的功能模块,需要考虑代码复用和重构的问题,以提高代码的重用性、可读性和可维护性。这需要合理的设计架构和代码组织结构,并使用适当的设计模式和最佳实践。
五、总结。
一、对代码:
对于Java代码,我自身是带有敬意的,因为C语言考试的不及格,多少对面向对象程序设计是抱有巨大的忧患意识的,所以在做作业的时候不敢怠慢。听老师的话先在IDEA(老师说也可以在eclipse)里先写好再复制粘贴到PTA里,我也听说了一些同学直接在PTA里写,(逻辑,运行等)错误百出,浪费了很多时间。虽然真的学得特别吃力,但我会认真听老师讲课,去看别人写的代码,看别人下很大功夫才想出来的设计和算法。
二、对自己:
只能说尽力去学吧,Java本身就是一个十分庞大的东西,不能全指望老师上课讲的那些语法,还要去找资源,自己去B站找视频看。也是听取了厉害大佬的建议:时刻提醒自己这门专业本身就不是以学习语法为重点。处理问题时一头扎进问题中,忽略其他细节,在错误的方向上疯狂进发,然后写出来一堆没意义的代码,为了避免这种情况发生,老师讲到的东西一定要注意,老师上课讲的某些点很不经意,但都是他这么多年写代码总结出来的经验,认真学吧,总有些收获!!!
三、对教师:
我的面向对象程序设计老师是罗老师,对我们很认真负责,上课也是一本正经地上,我只能说在他的课上有种回到高中的感觉……
四、对这门课:
个人认为这种形式还不错,课上老师输出大波干货,课下自己自行摸索,也有同学老师之间的交流协作过程,虽然这样挺辛苦,但吃得苦中苦方为人上人嘛
还有就是这门课的考查方式,不是像上学期C语言那样手写代码,而是真正地在笔记本上敲代码,考察编程能力,真的是泰裤辣!