面向对象程序设计题目集总结blog2-22206110-胡瑞杰
一、前言
第二次在博客园上发布面向对象程序设计题目集的总结博客。经过几周的学习,面向对象的理念更加深入。虽然已经学了些面向对象程序设计,学好这部分内容还是有较大难度。
- 关于知识点
本次的题目集所体现的知识点已经不仅限于Java的语法知识,还需要考虑设计问题,不能看到题目就开始进行代码编写,需要考虑类和类之间的关系,题目的代码量也较于前几次提升了不少。题目集四主要还是语法的巩固,学会去使用一些新的知识,例如题目集中的第七题中使用一些新的类来解决问题。题目集五前四题是关于正则表达式的知识点,后两题是日期问题,需要用到类的聚合关系。题目集六主要涉及类的继承与多态的内容,后面的有些题目未给出类图,需要自行设计考虑类之间的关系。
- 关于题量
题量比较适中,平均每次题目集都是5-6题,除了有些题目不大好设计比较花时间。
- 关于难度
由于涉及了面向对象设计原理和法则(例如遵从迪米特法则、开闭原则等),难度较于前几次自然有所增加。但是在部分题目有类图的情况下,难度适中,除了一些题目叙述较长未能进行较好的设计。
二、设计与分析
- 题目集三7-1 菜单计价程序-3
设计点菜计价程序,根据输入的信息,计算并输出总价格。
输入内容按先后顺序包括两部分:菜单、订单,最后以"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+英文空格+桌号+“:”+英文空格+当前桌的总价
本次题目不考虑其他错误情况,如:桌号、菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的后续作业中会做要求。
输入样例:
麻婆豆腐 12 油淋生菜 9 table 1 2023/3/22 12/2/3 1 麻婆豆腐 2 2 2 油淋生菜 1 3 end
输出样例:
table 1: 1 麻婆豆腐 36 2 油淋生菜 27 table 1: 38
输入样例1:
麻婆豆腐 12 油淋生菜 9 table 1 2023/3/22 17/0/0 1 麻婆豆腐 2 2 2 油淋生菜 1 3 1 delete end
输出样例1:
table 1: 1 麻婆豆腐 36 2 油淋生菜 27 table 1: 22
输入样例2:
table 1: 1 麻婆豆腐 36 2 油淋生菜 27 table 1 out of opening hours
输出样例2:
table 1: 1 麻婆豆腐 36 2 油淋生菜 27 table 1 out of opening hours
输入样例3:
麻婆豆腐 12 油淋生菜 9 table 1 2022/12/5 15/03/02 1 麻婆豆腐 2 2 2 油淋生菜 1 3 3 麻辣鸡丝 1 2 5 delete 7 delete table 2 2022/12/3 15/03/02 1 麻婆豆腐 2 2 2 油淋生菜 1 3 3 麻辣鸡丝 1 2 7 delete end
输出样例3:
table 1: 1 麻婆豆腐 36 2 油淋生菜 27 麻辣鸡丝 does not exist delete error; delete error; table 2: 1 麻婆豆腐 36 2 油淋生菜 27 麻辣鸡丝 does not exist delete error; table 1 out of opening hours table 2: 63
输入样例4:
麻婆豆腐 12 油淋生菜 9 table 1 2022/12/3 19/5/12 1 麻婆豆腐 2 2 2 油淋生菜 1 3 3 麻辣鸡丝 1 2 table 2 2022/12/3 15/03/02 1 麻婆豆腐 2 2 2 油淋生菜 1 3 3 麻辣鸡丝 1 2 1 4 麻婆豆腐 1 1 7 delete end
输出样例4:
table 1: 1 麻婆豆腐 36 2 油淋生菜 27 麻辣鸡丝 does not exist table 2: 1 麻婆豆腐 36 2 油淋生菜 27 麻辣鸡丝 does not exist 4 table 2 pay for table 1 12 delete error; table 1: 63 table 2: 75
此题需要设计Dish类、Menu类、Record类、Order类,类与类之间存在着关联和依赖等关系。值得注意的是,本题需要考虑订单的格式问题,也需要考虑时间的问题,考虑营业时间及其折扣。题目叙述较长,需要记好需要实现的功能。
- 题目集五 7-5 日期问题面向对象设计(聚合一)
参考题目7-2的要求,设计如下几个类:DateUtil、Year、Month、Day,其中年、月、日的取值范围依然为:year∈[1900,2050] ,month∈[1,12] ,day∈[1,31] , 设计类图如下:
应用程序共测试三个功能:
- 求下n天
- 求前n天
- 求两个日期相差的天数
注意:严禁使用Java中提供的任何与日期相关的类与方法,并提交完整源码,包括主类及方法(已提供,不需修改)
输入格式:
有三种输入方式(以输入的第一个数字划分[1,3]):
- 1 year month day n //测试输入日期的下n天
- 2 year month day n //测试输入日期的前n天
- 3 year1 month1 day1 year2 month2 day2 //测试两个日期之间相差的天数
输出格式:
- 当输入有误时,输出格式如下:
Wrong Format
- 当第一个数字为1且输入均有效,输出格式如下:
year-month-day
- 当第一个数字为2且输入均有效,输出格式如下:
year-month-day
- 当第一个数字为3且输入均有效,输出格式如下:
天数值
输入样例1:
3 2014 2 14 2020 6 14
输出样例1:
2312
输入样例2:
2 1935 2 17 125340
输出样例2:
1591-12-17
输入样例3:
1 1999 3 28 6543
输出样例3:
2017-2-24
输入样例4:
0 2000 5 12 30
输出样例4:
Wrong Format
本题需要设计出Year、Month、Day三个类对时间进行处理,在类中判断有效性、获取前一天(月、年)或者后一天(月、年),在DateUtil类中实现输出日期格式、判断日期的有效性、获取前n天和后n天的日期、获取两个日期间隔的天数等功能。这道题主要需要注意的是判断日期的有效性、闰年时二月份有29天、在增减天数时需要判断是否为日期、月份的最大最小值进行日期的改动。设计好获取日期的前后n天后,可以直接调用这个方法来完成获取日期间的天数。
代码实现:
1 import java.util.Scanner; 2 class Main { 3 public static void main(String[] args) { 4 Scanner input = new Scanner(System.in); 5 int year = 0; 6 int month = 0; 7 int day = 0; 8 9 int choice = input.nextInt(); 10 11 if (choice == 1) { // test getNextNDays method 12 int m = 0; 13 year = Integer.parseInt(input.next()); 14 month = Integer.parseInt(input.next()); 15 day = Integer.parseInt(input.next()); 16 17 DateUtil date = new DateUtil(year, month, day); 18 19 if (!date.checkInputValidity()) { 20 System.out.print("Wrong Format"); 21 System.exit(0); 22 } 23 24 m = input.nextInt(); 25 26 if (m < 0) { 27 System.out.print("Wrong Format"); 28 System.exit(0); 29 } 30 System.out.println(date.getNextDays(m).showDate()); 31 } else if (choice == 2) { // test getPreviousNDays method 32 int n = 0; 33 year = Integer.parseInt(input.next()); 34 month = Integer.parseInt(input.next()); 35 day = Integer.parseInt(input.next()); 36 37 DateUtil date = new DateUtil(year, month, day); 38 39 if (!date.checkInputValidity()) { 40 System.out.println("Wrong Format"); 41 System.exit(0); 42 } 43 44 n = input.nextInt(); 45 46 if (n < 0) { 47 System.out.println("Wrong Format"); 48 System.exit(0); 49 } 50 System.out.println(date.getPreviousDays(n).showDate()); 51 } else if (choice == 3) { //test getDaysofDates method 52 year = Integer.parseInt(input.next()); 53 month = Integer.parseInt(input.next()); 54 day = Integer.parseInt(input.next()); 55 56 int anotherYear = Integer.parseInt(input.next()); 57 int anotherMonth = Integer.parseInt(input.next()); 58 int anotherDay = Integer.parseInt(input.next()); 59 60 DateUtil fromDate = new DateUtil(year, month, day); 61 DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay); 62 63 if (fromDate.checkInputValidity() && toDate.checkInputValidity()) { 64 System.out.print(fromDate.getDaysofDates(toDate)); 65 } else { 66 System.out.print("Wrong Format"); 67 System.exit(0); 68 } 69 } 70 else{ 71 System.out.print("Wrong Format"); 72 System.exit(0); 73 } 74 75 } 76 } 77 78 79 class Year { 80 private int value; 81 public Year() { 82 83 }//无参构造方法 84 85 public Year(int value) { 86 this.value = value; 87 }//有参构造方法 88 89 public int getValue() { 90 return value; 91 } 92 93 public void setValue(int value) { 94 this.value = value; 95 } 96 97 public boolean isLeapYear() { 98 boolean isLeapYear; 99 isLeapYear = ((value % 4 == 0 && value % 100 != 0) || value % 400 == 0); 100 return isLeapYear; 101 }//判断是否为闰年 102 103 public boolean validate() { 104 boolean validate; 105 validate = (value >= 1900 && value <= 2050); 106 return validate; 107 }//判断年份有效性 108 109 public void yearIncrement() { 110 value++; 111 }//年份加1 112 113 public void yearReduction() { 114 value--; 115 }//年份减1 116 } 117 118 class Month { 119 private int value; 120 private Year year; 121 public Month() { 122 123 }//无参构造方法 124 125 public Month(int yearValue, int monthValue) { 126 this.year = new Year(yearValue); 127 this.value = monthValue; 128 }//有参构造方法 129 130 public int getValue() { 131 return value; 132 } 133 134 public void setValue(int value) { 135 this.value = value; 136 } 137 138 public Year getYear() { 139 return year; 140 } 141 142 public void setYear(Year year) { 143 this.year = year; 144 } 145 146 public void resetMin() { 147 value = 1; 148 }//设置月份最小值 149 150 public void resetMax() { 151 value = 12; 152 }//设置月份最大值 153 154 public boolean validate() { 155 boolean validate; 156 validate = (value >= 1 && value <= 12); 157 return validate; 158 }//判断月份有效性 159 160 public void monthIncrement() { 161 if (validate()) { 162 if (value == 12) { 163 getYear().yearIncrement(); 164 resetMin(); 165 } else { 166 value++; 167 } 168 } 169 }//月份加1 170 171 public void monthReduction() { 172 if (validate()) { 173 if (value == 1) { 174 getYear().yearReduction(); 175 resetMax(); 176 } else { 177 value--; 178 } 179 } 180 }//月份减1 181 } 182 183 class Day { 184 private int value; 185 private Month month; 186 private int[] mon_maxnum = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 187 public Day() { 188 189 }//无参构造方法 190 191 public Day(int yearValue, int monthValue, int dayValue) { 192 this.month = new Month(yearValue, monthValue); 193 this.value = dayValue; 194 }//有参构造方法 195 196 public int getValue() { 197 return value; 198 } 199 200 public void setValue(int value) { 201 this.value = value; 202 } 203 204 public Month getMonth() { 205 return month; 206 } 207 208 public void setMonth(Month month) { 209 this.month = month; 210 } 211 212 public void resetMin() { 213 value = 1; 214 }//设置日期最小值 215 216 public void resetMax() { 217 if (validate()) { 218 value = mon_maxnum[month.getValue() - 1]; 219 }//设置日期为该月最大日期 220 } 221 222 public boolean validate() { 223 boolean validate = false; 224 if (month.getValue() == 2 && getMonth().getYear().isLeapYear()) { 225 mon_maxnum[1] = 29; 226 } else { 227 mon_maxnum[1] = 28; 228 } 229 if (getMonth().validate()) { 230 validate = (value >= 1 && value <= mon_maxnum[month.getValue() - 1]); 231 } 232 return validate; 233 }//判断日期有效性 234 235 public void dayIncrement() { 236 value++; 237 if (!validate()){ 238 resetMin(); 239 getMonth().monthIncrement(); 240 } 241 }//日期加1,月底时月份加1日期为1 242 243 public void dayReduction() { 244 if (value == 1) { 245 getMonth().monthReduction(); 246 resetMax(); 247 } else { 248 value--; 249 } 250 }//日期减1,当为日期为1时,月份减1,若是1月,年份减1 251 } 252 253 class DateUtil { 254 private Day day; 255 public DateUtil() { 256 257 } 258 259 public DateUtil(int d, int m, int y) { 260 this.day = new Day(d, m, y); 261 } 262 263 public Day getDay() { 264 return day; 265 } 266 267 public void setDay(Day d) { 268 this.day = d; 269 } 270 271 public boolean checkInputValidity() { 272 boolean checkInputValidity; 273 checkInputValidity = (getDay().validate() && getDay().getMonth().validate() 274 && getDay().getMonth().getYear().validate()); 275 return checkInputValidity; 276 }//判断日期有效性 277 278 public boolean compareDates(DateUtil date) { 279 return (getDay().getMonth().getYear().getValue() < date.getDay().getMonth().getYear().getValue() 280 || (getDay().getMonth().getYear().getValue() == date.getDay().getMonth().getYear().getValue() && getDay().getMonth().getValue() < date.getDay().getMonth().getValue()) 281 || (getDay().getMonth().getYear().getValue() == date.getDay().getMonth().getYear().getValue() && getDay().getMonth().getValue() == date.getDay().getMonth().getValue() 282 && getDay().getValue() < date.getDay().getValue())); 283 }//比较日期比原日期大 284 285 public boolean equalTwoDates(DateUtil date) { 286 return (getDay().getMonth().getYear().getValue() == date.getDay().getMonth().getYear().getValue() 287 && getDay().getMonth().getValue() == date.getDay().getMonth().getValue() 288 && getDay().getValue() == date.getDay().getValue()); 289 }//比较两日期相等 290 291 public String showDate() { 292 return (day.getMonth().getYear().getValue() + "-" + day.getMonth().getValue() + "-" + day.getValue()); 293 }//返回日期格式 294 295 public DateUtil getNextDays(int n) { 296 for (int i = 0; i < n; i++) { 297 getDay().dayIncrement(); 298 } 299 return new DateUtil(this.day.getMonth().getYear().getValue(), 300 this.day.getMonth().getValue(), this.day.getValue()); 301 }//日期后n天 302 303 public DateUtil getPreviousDays(int n) { 304 for (int i = 0; i < n; i++) { 305 getDay().dayReduction(); 306 } 307 return new DateUtil(this.day.getMonth().getYear().getValue(), 308 this.day.getMonth().getValue(), this.day.getValue()); 309 }//日期前n天 310 311 public int getDaysofDates(DateUtil date) { 312 int days = 0; 313 if (compareDates(date)) { 314 while(!equalTwoDates(date)) { 315 getDay().dayIncrement(); 316 days++; 317 } 318 } else { 319 while(!equalTwoDates(date)) { 320 getDay().dayReduction(); 321 days++; 322 } 323 } 324 return days; 325 }//两日期相差天数 326 }
sourcemonitor代码分析:
跟据sourcemonitor上分析,大部分指标处于合格范围,但是最大复杂度有点偏高。
- 题目集五7-6 日期问题面向对象设计(聚合二)
参考题目7-3的要求,设计如下几个类:DateUtil、Year、Month、Day,其中年、月、日的取值范围依然为:year∈[1820,2020] ,month∈[1,12] ,day∈[1,31] , 设计类图如下:
应用程序共测试三个功能:
- 求下n天
- 求前n天
- 求两个日期相差的天数
注意:严禁使用Java中提供的任何与日期相关的类与方法,并提交完整源码,包括主类及方法(已提供,不需修改)
输入格式:
有三种输入方式(以输入的第一个数字划分[1,3]):
- 1 year month day n //测试输入日期的下n天
- 2 year month day n //测试输入日期的前n天
- 3 year1 month1 day1 year2 month2 day2 //测试两个日期之间相差的天数
输出格式:
- 当输入有误时,输出格式如下:
Wrong Format
- 当第一个数字为1且输入均有效,输出格式如下:
year1-month1-day1 next n days is:year2-month2-day2
- 当第一个数字为2且输入均有效,输出格式如下:
year1-month1-day1 previous n days is:year2-month2-day2
- 当第一个数字为3且输入均有效,输出格式如下:
The days between year1-month1-day1 and year2-month2-day2
输入样例1:
在这里给出一组输入。例如:
3 2014 2 14 2020 6 14
输出样例1:
在这里给出相应的输出。例如:
The days between 2014-2-14 and 2020-6-14 are:2312
输入样例2:
在这里给出一组输入。例如:
2 1834 2 17 7821
输出样例2:
在这里给出相应的输出。例如:
1834-2-17 previous 7821 days is:1812-9-19
输入样例3:
在这里给出一组输入。例如:
1 1999 3 28 6543
输出样例3:
在这里给出相应的输出。例如:
1999-3-28 next 6543 days is:2017-2-24
输入样例4:
在这里给出一组输入。例如:
0 2000 5 12 30
输出样例4:
在这里给出相应的输出。例如:
Wrong Format
同上题要求类似,但是值得注意的是需要改变年份的取值范围。除此之外,为了满足题目条件,还需改动类之间的关系,类中的方法也是需要改变的。实现需求的方法仍然是放在DateUtil类中进行,增加了setDayMin、setDayMax等方法。
代码实现:
1 import java.util.Scanner; 2 public class Main { 3 public static void main(String[] args) { 4 Scanner input = new Scanner(System.in); 5 int year = 0; 6 int month = 0; 7 int day = 0; 8 9 int choice = input.nextInt(); 10 11 if (choice == 1) { // test getNextNDays method 12 int m = 0; 13 year = Integer.parseInt(input.next()); 14 month = Integer.parseInt(input.next()); 15 day = Integer.parseInt(input.next()); 16 17 DateUtil date = new DateUtil(year, month, day); 18 19 if (!date.checkInputValidity()) { 20 System.out.print("Wrong Format"); 21 System.exit(0); 22 } 23 24 m = input.nextInt(); 25 26 if (m < 0) { 27 System.out.print("Wrong Format"); 28 System.exit(0); 29 } 30 System.out.print(date.getYear().getValue() + "-" + date.getMonth().getValue() + "-" + date.getDay().getValue() + " next " + m + " days is:"); 31 System.out.println(date.getNextNDays(m).showDate()); 32 } else if (choice == 2) { // test getPreviousNDays method 33 int n = 0; 34 year = Integer.parseInt(input.next()); 35 month = Integer.parseInt(input.next()); 36 day = Integer.parseInt(input.next()); 37 38 DateUtil date = new DateUtil(year, month, day); 39 40 if (!date.checkInputValidity()) { 41 System.out.println("Wrong Format"); 42 System.exit(0); 43 } 44 45 n = input.nextInt(); 46 47 if (n < 0) { 48 System.out.println("Wrong Format"); 49 System.exit(0); 50 } 51 System.out.print(date.getYear().getValue() + "-" + date.getMonth().getValue() + "-" + date.getDay().getValue() + " previous " + n + " days is:"); 52 System.out.println(date.getPreviousNDays(n).showDate()); 53 } else if (choice == 3) { //test getDaysofDates method 54 year = Integer.parseInt(input.next()); 55 month = Integer.parseInt(input.next()); 56 day = Integer.parseInt(input.next()); 57 58 int anotherYear = Integer.parseInt(input.next()); 59 int anotherMonth = Integer.parseInt(input.next()); 60 int anotherDay = Integer.parseInt(input.next()); 61 62 DateUtil fromDate = new DateUtil(year, month, day); 63 DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay); 64 65 if (fromDate.checkInputValidity() && toDate.checkInputValidity()) { 66 System.out.print("The days between " + fromDate.showDate() + 67 " and " + toDate.showDate() + " are:" + fromDate.getDaysofDates(toDate)); 68 } else { 69 System.out.print("Wrong Format"); 70 System.exit(0); 71 } 72 } 73 else{ 74 System.out.print("Wrong Format"); 75 System.exit(0); 76 } 77 78 } 79 } 80 81 82 class Year { 83 private int value; 84 public Year() { 85 86 }//无参构造方法 87 88 public Year(int value) { 89 this.value = value; 90 }//有参构造方法 91 92 public int getValue() { 93 return value; 94 } 95 96 public void setValue(int value) { 97 this.value = value; 98 } 99 100 public boolean isLeapYear() { 101 boolean isLeapYear; 102 isLeapYear = ((value % 4 == 0 && value % 100 != 0) || value % 400 == 0); 103 return isLeapYear; 104 }//判断是否为闰年 105 106 public boolean validate() { 107 boolean validate; 108 validate = (value >= 1820 && value <= 2020); 109 return validate; 110 }//判断年份有效性 111 112 public void yearIncrement() { 113 value++; 114 }//年份加1 115 116 public void yearReduction() { 117 value--; 118 }//年份减1 119 } 120 121 class Month { 122 private int value; 123 public Month() { 124 125 }//无参构造方法 126 127 public Month(int value) { 128 this.value = value; 129 }//有参构造方法 130 131 public int getValue() { 132 return value; 133 } 134 135 public void setValue(int value) { 136 this.value = value; 137 } 138 139 public void resetMin() { 140 value = 1; 141 }//设置月份最小值 142 143 public void resetMax() { 144 value = 12; 145 }//设置月份最大值 146 147 public boolean validate() { 148 boolean validate; 149 validate = (value >= 1 && value <= 12); 150 return validate; 151 }//判断月份有效性 152 153 public void monthIncrement() { 154 value++; 155 }//月份加1 156 157 public void monthReduction() { 158 value--; 159 }//月份减1 160 } 161 162 class Day { 163 private int value; 164 public Day() { 165 166 }//无参构造方法 167 168 public Day(int value) { 169 this.value = value; 170 }//有参构造方法 171 172 public int getValue() { 173 return value; 174 } 175 176 public void setValue(int value) { 177 this.value = value; 178 } 179 180 public void dayIncrement() { 181 value++; 182 }//日期加1 183 184 public void dayReduction() { 185 value--; 186 }//日期减1 187 } 188 189 class DateUtil { 190 private Year year; 191 private Month month; 192 private Day day; 193 private int[] mon_maxnum = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 194 195 public DateUtil() { 196 } 197 198 public DateUtil(int y, int m, int d) { 199 this.year = new Year(y); 200 this.month = new Month(m); 201 this.day = new Day(d); 202 } 203 204 public Year getYear() { 205 return year; 206 } 207 208 public void setYear(Year year) { 209 this.year = year; 210 } 211 212 public Month getMonth() { 213 return month; 214 } 215 216 public void setMonth(Month month) { 217 this.month = month; 218 } 219 220 public Day getDay() { 221 return day; 222 } 223 224 public void setDay(Day day) { 225 this.day = day; 226 } 227 228 public void setDayMin() { 229 day.setValue(1); 230 } 231 232 public void setDayMax() { 233 if (year.isLeapYear()) { 234 mon_maxnum[1] = 29; 235 } else { 236 mon_maxnum[1] = 28; 237 } 238 day.setValue(mon_maxnum[month.getValue() - 1]); 239 } 240 241 public boolean checkInputValidity() { 242 boolean checkInputValidity; 243 boolean day_validate = false; 244 if (year.isLeapYear()) { 245 mon_maxnum[1] = 29; 246 } else { 247 mon_maxnum[1] = 28; 248 } 249 if (getMonth().validate()) { 250 day_validate = (day.getValue() >= 1 && day.getValue() <= mon_maxnum[month.getValue() - 1]); 251 } 252 checkInputValidity = (day_validate && month.validate() && year.validate()); 253 return checkInputValidity; 254 }//判断日期有效性 255 256 public boolean compareDates(DateUtil date) { 257 return (year.getValue() < date.year.getValue() 258 || (year.getValue() == date.year.getValue() && month.getValue() < date.month.getValue()) 259 || (year.getValue() == date.year.getValue() 260 && month.getValue() == date.month.getValue() 261 && day.getValue() < date.day.getValue())); 262 }//比较日期和原日期大小 263 264 public boolean equalTwoDates(DateUtil date) { 265 return (year.getValue() == date.year.getValue() && month.getValue() == date.month.getValue() 266 && day.getValue() == date.day.getValue()); 267 }//比较两日期相等 268 269 public String showDate() { 270 return (year.getValue() + "-" + month.getValue() + "-" + day.getValue()); 271 }//返回日期格式 272 273 public DateUtil getNextNDays(int n) { 274 for (int i = 0; i < n; i++) { 275 getDay().dayIncrement(); 276 if (year.isLeapYear()) { 277 mon_maxnum[1] = 29; 278 } else { 279 mon_maxnum[1] = 28; 280 } 281 if (day.getValue() > mon_maxnum[month.getValue() - 1]) { 282 month.monthIncrement(); 283 if (month.getValue() > 12) { 284 year.yearIncrement(); 285 month.resetMin(); 286 } 287 setDayMin(); 288 } 289 290 } 291 return new DateUtil(year.getValue(), month.getValue(), day.getValue()); 292 }//日期后n天 293 294 public DateUtil getPreviousNDays(int n) { 295 for (int i = 0; i < n; i++) { 296 getDay().dayReduction(); 297 if (day.getValue() < 1) { 298 month.monthReduction(); 299 if (month.getValue() < 1) { 300 year.yearReduction(); 301 month.resetMax(); 302 setDayMax(); 303 } 304 setDayMax(); 305 } 306 307 } 308 return new DateUtil(year.getValue(), month.getValue(), day.getValue()); 309 }//日期前n天 310 311 public int getDaysofDates(DateUtil date) { 312 int days = 0; 313 if (compareDates(date)) { 314 while(!equalTwoDates(date)) { 315 getDay().dayIncrement(); 316 if (year.isLeapYear()) { 317 mon_maxnum[1] = 29; 318 } else { 319 mon_maxnum[1] = 28; 320 } 321 if (day.getValue() > mon_maxnum[month.getValue() - 1]) { 322 month.monthIncrement(); 323 if (month.getValue() > 12) { 324 year.yearIncrement(); 325 month.resetMin(); 326 } 327 setDayMin(); 328 } 329 days++; 330 } 331 } else { 332 while(!equalTwoDates(date)) { 333 getDay().dayReduction(); 334 if (day.getValue() < 1) { 335 month.monthReduction(); 336 if (month.getValue() < 1) { 337 year.yearReduction(); 338 month.resetMax(); 339 setDayMax(); 340 } 341 setDayMax(); 342 } 343 days++; 344 } 345 } 346 return days; 347 }//两日期相差天数 348 }
sourcemonitor代码分析:
- 题目集六7-4 ATM机类结构设计(一)
设计ATM仿真系统,具体要求参见作业说明。
输入格式:
每一行输入一次业务操作,可以输入多行,最终以字符#终止。具体每种业务操作输入格式如下:
- 存款、取款功能输入数据格式:
卡号 密码 ATM机编号 金额
(由一个或多个空格分隔),
其中,当金额大于0时,代表取款,否则代表存款。 - 查询余额功能输入数据格式:
卡号
输出格式:
①输入错误处理
- 如果输入卡号不存在,则输出
Sorry,this card does not exist.
。 - 如果输入ATM机编号不存在,则输出
Sorry,the ATM's id is wrong.
。 - 如果输入银行卡密码错误,则输出
Sorry,your password is wrong.
。 - 如果输入取款金额大于账户余额,则输出
Sorry,your account balance is insufficient.
。 - 如果检测为跨行存取款,则输出
Sorry,cross-bank withdrawal is not supported.
。
②取款业务输出
输出共两行,格式分别为:
[用户姓名]在[银行名称]的[ATM编号]上取款¥[金额]
当前余额为¥[金额]
其中,[]说明括起来的部分为输出属性或变量,金额均保留两位小数。
③存款业务输出
输出共两行,格式分别为:
[用户姓名]在[银行名称]的[ATM编号]上存款¥[金额]
当前余额为¥[金额]
其中,[]说明括起来的部分为输出属性或变量,金额均保留两位小数。
④查询余额业务输出
¥[金额]
金额保留两位小数。
输入样例1:
在这里给出一组输入。例如:
6222081502001312390 88888888 06 -500.00
#
输出样例1:
在这里给出相应的输出。例如:
张无忌在中国工商银行的06号ATM机上存款¥500.00
当前余额为¥10500.00
输入样例2:
在这里给出一组输入。例如:
6217000010041315709 88888888 02 3500.00
#
输出样例2:
在这里给出相应的输出。例如:
杨过在中国建设银行的02号ATM机上取款¥3500.00
当前余额为¥6500.00
输入样例3:
在这里给出一组输入。例如:
6217000010041315715
#
输出样例3:
在这里给出相应的输出。例如:
¥10000.00
输入样例4:
在这里给出一组输入。例如:
6222081502001312390 88888888 06 -500.00
6222081502051320786 88888888 06 1200.00
6217000010041315715 88888888 02 1500.00
6217000010041315709 88888888 02 3500.00
6217000010041315715
#
输出样例4:
在这里给出相应的输出。例如:
张无忌在中国工商银行的06号ATM机上存款¥500.00
当前余额为¥10500.00
韦小宝在中国工商银行的06号ATM机上取款¥1200.00
当前余额为¥8800.00
杨过在中国建设银行的02号ATM机上取款¥1500.00
当前余额为¥8500.00
杨过在中国建设银行的02号ATM机上取款¥3500.00
当前余额为¥5000.00
¥5000.00
本题需要实现模拟简易ATM。值得注意的是,每个人能有多个银行账户,每一个账户可以对应多个卡号,金额是用户总金额而不是每个账号的金额,需要设计多个类来实现,类之间存在关联、聚合、继承等关系。
代码实现:
查看代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String str;
Account[] account = new Account[10];
account[0] = new Account("杨过", "中国建设银行", "3217000010041315709", 10000.00, "6217000010041315709");
account[1] = new Account("杨过", "中国建设银行", "3217000010041315709", 10000.00, "6217000010041315715");
account[2] = new Account("杨过", "中国建设银行", "3217000010041315715", 10000.00, "6217000010041315718");
account[3] = new Account("郭靖", "中国建设银行", "3217000010051320007 ", 10000.00,"6217000010051320007");
account[4] = new Account("张无忌", "中国工商银行", "3222081502001312389", 10000.00,"6222081502001312389");
account[5] = new Account("张无忌", "中国工商银行", "3222081502001312390", 10000.00,"6222081502001312390");
account[6] = new Account("张无忌", "中国工商银行", "3222081502001312399", 10000.00, "6222081502001312399");
account[7] = new Account("张无忌", "中国工商银行", "3222081502001312399", 10000.00, "6222081502001312400");
account[8] = new Account("韦小宝", "中国工商银行", "3222081502051320785", 10000.00,"6222081502051320785");
account[9] = new Account("韦小宝", "中国工商银行", "3222081502051320786", 10000.00,"6222081502051320786");
while (true) {
str = input.nextLine();
if (str.equals("#")) {
break;
}
Bank bank = new Bank();
Card card = new Card();
ATM atm = new ATM();
Money money = new Money();
User user = new User();
String[] info = str.split("\\s+");
card.setCardNumber(info[0]);
user.setPassWord(info[1]);
atm.setATMNumber(info[2]);
money.setMoney(Double.parseDouble(info[3]));
boolean flag = false;
for (int i = 0; i <= 9; i ++) {
if (card.getCardNumber().equals(account[i].getCardNumber())) {
flag = true;
if (user.isPassWord()) {
if (atm.isATMNumber()) {
if (account[i].getBankName().equals(bank.getBankName(atm))){
money.setBalance(account[i].getBalance());
money.deposit();
if (money.judge()) {
if (money.getMoney() > 0) {
System.out.println(account[i].getUserName() + "在" + account[i].getBankName() + "的" + atm.getATMNumber() + "号ATM机上取款¥" + String.format("%.2f", Math.abs(money.getMoney())));
} else {
System.out.println(account[i].getUserName() + "在" + account[i].getBankName() + "的" + atm.getATMNumber() + "号ATM机上存款¥" + String.format("%.2f", Math.abs(money.getMoney())));
}
System.out.println("当前余额为¥" + String.format("%.2f", money.getBalance()));
}
} else {
System.out.println("Sorry,cross-bank withdrawal is not supported.");
}
}
} else {
System.out.println("Sorry,your password is wrong.");
}
}
}
if (!flag) {
System.out.println("Sorry,this card does not exist.");
}
}
}
}
class Bank {
private String bankName;
public Bank(String bankName) {
this.bankName = bankName;
}
public Bank() {
}
public String getBankName(ATM atm) {
if (atm.getATMNumber().equals("01") || atm.getATMNumber().equals("02")
|| atm.getATMNumber().equals("03") || atm.getATMNumber().equals("04")) {
bankName = "中国建设银行";
} else if (atm.getATMNumber().equals("05") || atm.getATMNumber().equals("06")){
bankName = "中国工商银行";
} else {
System.out.println("Sorry,the ATM's id is wrong.");
}
return bankName;
}
}
class User {
private String passWord;
public User() {
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public User(String passWord) {
this.passWord = passWord;
}
public boolean isPassWord() {
return getPassWord().equals("88888888");
}
}
class Account {
private String accountNumber;
private String bankName;
private String cardNumber;
private String userName;
private double balance;
public Account(String userName, String bankName, String accountNumber, double balance, String cardNumber) {
this.accountNumber = accountNumber;
this.bankName = bankName;
this.cardNumber = cardNumber;
this.userName = userName;
this.balance = balance;
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public String getBankName() {
return bankName;
}
public void setBankName(String bankName) {
this.bankName = bankName;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
class ATM {
private String ATMNumber;
public ATM(String ATMNumber) {
this.ATMNumber = ATMNumber;
}
public ATM() {
}
public String getATMNumber() {
return this.ATMNumber;
}
public void setATMNumber(String ATMNumber) {
this.ATMNumber = ATMNumber;
}
public boolean isATMNumber() {
if (ATMNumber.matches("^0[1-6]$")) {
return true;
} else {
System.out.println("Sorry,the ATM's id is wrong.");
return false;
}
}
}
class Card {
private String cardNumber;
public Card() {
}
public Card(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
}
class Money {
private double balance;
private double money;
public Money(double balance) {
this.balance = balance;
}
public Money() {
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void deposit() {
this.balance -= money;
}
public boolean judge() {
if (balance < 0) {
System.out.println("Sorry,your account balance is insufficient.");
return false;
} else {
return true;
}
}
}
- 题目集六7-5 ATM机类结构设计(二)
输入格式:
每一行输入一次业务操作,可以输入多行,最终以字符#终止。具体每种业务操作输入格式如下:
- 取款功能输入数据格式:
卡号 密码 ATM机编号 金额
(由一个或多个空格分隔) - 查询余额功能输入数据格式:
卡号
输出格式:
①输入错误处理
- 如果输入卡号不存在,则输出
Sorry,this card does not exist.
。 - 如果输入ATM机编号不存在,则输出
Sorry,the ATM's id is wrong.
。 - 如果输入银行卡密码错误,则输出
Sorry,your password is wrong.
。 - 如果输入取款金额大于账户余额,则输出
Sorry,your account balance is insufficient.
。
②取款业务输出
输出共两行,格式分别为:
业务:取款 [用户姓名]在[银行名称]的[ATM编号]上取款¥[金额]
当前余额为¥[金额]
其中,[]说明括起来的部分为输出属性或变量,金额均保留两位小数。
③查询余额业务输出
业务:查询余额 ¥[金额]
金额保留两位小数。
输入样例1:
在这里给出一组输入。例如:
6222081502001312390 88888888 06 500.00
#
输出样例1:
在这里给出相应的输出。例如:
业务:取款 张无忌在中国工商银行的06号ATM机上取款¥500.00
当前余额为¥9500.00
输入样例2:
在这里给出一组输入。例如:
6217000010041315709 88888888 06 3500.00
#
输出样例2:
在这里给出相应的输出。例如:
业务:取款 杨过在中国工商银行的06号ATM机上取款¥3500.00
当前余额为¥6395.00
输入样例3:
在这里给出一组输入。例如:
6217000010041315715
#
输出样例3:
在这里给出相应的输出。例如:
业务:查询余额 ¥10000.00
输入样例4:
在这里给出一组输入。例如:
6222081502001312390 88888888 01 500.00
6222081502051320786 88888888 06 1200.00
6217000010041315715 88888888 02 1500.00
6217000010041315709 88888888 02 3500.00
6217000010041315715
#
输出样例4:
在这里给出相应的输出。例如:
业务:取款 张无忌在中国建设银行的01号ATM机上取款¥500.00
当前余额为¥9490.00
业务:取款 韦小宝在中国工商银行的06号ATM机上取款¥1200.00
当前余额为¥8800.00
业务:取款 杨过在中国建设银行的02号ATM机上取款¥1500.00
当前余额为¥8500.00
业务:取款 杨过在中国建设银行的02号ATM机上取款¥3500.00
当前余额为¥5000.00
业务:查询余额 ¥5000.00
输入样例5:
在这里给出一组输入。例如:
6640000010045442002 88888888 09 3000
6640000010045442002 88888888 06 8000
6640000010045442003 88888888 01 10000
6640000010045442002
#
输出样例5:
在这里给出相应的输出。例如:
业务:取款 张三丰在中国农业银行的09号ATM机上取款¥3000.00
当前余额为¥6880.00
业务:取款 张三丰在中国工商银行的06号ATM机上取款¥8000.00
当前余额为¥-1416.00
业务:取款 张三丰在中国建设银行的01号ATM机上取款¥10000.00
当前余额为¥-11916.00
业务:查询余额 ¥-11916.00
本题较于上道题又添加了要求,上题不可进行跨行业务,而此题可以。除此之外,还添加了银行个数、ATM机个数、手续费、新的用户。值得注意的是,银行卡分为借记卡和信用卡。整体的需求比上题增加了不少,难度自然也上升了。
三、踩坑心得
题目集5 7-5日期问题
在写日期有效性的时候忘记加小于等于,只是日期大于等于1小于该月份最大日期导致问题。
但是在改正后发现其他问题:
这些原本正确的测试点出了问题。
测试样例:
代码运行后的结果:
与正确答案相差了不少。跟据答案的差值不难推断出,这相差76的问题就是出现在月份上。2014年3月份离2020年6月份刚好相差76个月,这就意味着每个月份都多计算了一天。
问题所在:
这是增加天数的写法。若是根据这个写法,当天数到达最后一天的时候仍然是有效的,并没有超出月份日期最大值。因此更换为:
若是先加天数,再来进行判断就可避免这个问题。一旦到了最大天数后,首先天数加一,若是超出了范围则设置为1。
之前没有出现这个问题,是因为设置的范围到达不了最大值而是当月天数最大值减一,所以可以先判断是否有效再加天数。但是这样写的问题就是,判断天数有效性根本到不了原本月份的最后一天。例如,在一月份的30日时,因为日期有效先进行加1,此时的日期为31,但是再次判断就不在范围之内,就直接将日期改成1。以此类推,每一个月都会出现这样的问题,在到达月份最大天数前,日期就改成1,每个月都会少算一天而在实际的日期后增加少的天数,故错误答案多了和月份数相等的天数。
改正后的结果:
四、改进建议
- 在许多题目中,为了使程序运行结果正确,代码中会存在不合理的地方,例如有些设计的方法没有用上和将要实现的功能放在一个方法中实现,违背单一职责原则,一个方法中可能既要判断又要输出。其次,代码中的有些语句可能会比较复杂,运用了多个循环和if条件语句,使得代码的复杂度大大提升,代码的可读性也因此降低。由于设计时考虑不周,许多题目也会出现代码写得很死的现象,没有考虑其扩展性,未能给代码一定的扩展空间。若是需要在源代码上增加其他的功能,只能修改源代码来实现,并没有遵循开闭原则。
五、总结
-
- 当遇到许多题目自身比较复杂的情况时,我在理解、设计方面仍有诸多不足,抽象思维仍需要不断地锻炼,对于细节处理方面还是会出现问题。这几次的题目集可以很明显地看出代码量在不断上升,难度也在不断加大。从原本的给出类图到不给出类图自行设计,这就需要花费很多时间来设计,设计出现问题,代码实现这一环节自然就会跟着出问题,最后不断更改也只会越改越乱。
- 之后的题目难度也会不断上升,无论是从题目的复杂度还是代码量都是如此,今后还是应该注重这些方面,在线上、线下课堂、辅导书上多研究。