面向对象程序设计题目集总结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] , 设计类图如下:

 

应用程序共测试三个功能:

  1. 求下n天
  2. 求前n天
  3. 求两个日期相差的天数

注意:严禁使用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 }
View Code

 sourcemonitor代码分析:

 

 跟据sourcemonitor上分析,大部分指标处于合格范围,但是最大复杂度有点偏高。

  • 题目集五7-6 日期问题面向对象设计(聚合二)

参考题目7-3的要求,设计如下几个类:DateUtil、Year、Month、Day,其中年、月、日的取值范围依然为:year∈[1820,2020] ,month∈[1,12] ,day∈[1,31] , 设计类图如下:

应用程序共测试三个功能:

  1. 求下n天
  2. 求前n天
  3. 求两个日期相差的天数

注意:严禁使用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 }
View Code

sourcemonitor代码分析:

 

  • 题目集六7-4 ATM机类结构设计(一)

设计ATM仿真系统,具体要求参见作业说明。

OO作业8-1题目说明.pdf

输入格式:

每一行输入一次业务操作,可以输入多行,最终以字符#终止。具体每种业务操作输入格式如下:

  • 存款、取款功能输入数据格式:
    卡号 密码 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机类结构设计(二)

OO作业9-1题目说明.pdf

输入格式:

每一行输入一次业务操作,可以输入多行,最终以字符#终止。具体每种业务操作输入格式如下:

  • 取款功能输入数据格式:
    卡号 密码 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条件语句,使得代码的复杂度大大提升,代码的可读性也因此降低。由于设计时考虑不周,许多题目也会出现代码写得很死的现象,没有考虑其扩展性,未能给代码一定的扩展空间。若是需要在源代码上增加其他的功能,只能修改源代码来实现,并没有遵循开闭原则。

五、总结

    •   当遇到许多题目自身比较复杂的情况时,我在理解、设计方面仍有诸多不足,抽象思维仍需要不断地锻炼,对于细节处理方面还是会出现问题。这几次的题目集可以很明显地看出代码量在不断上升,难度也在不断加大。从原本的给出类图到不给出类图自行设计,这就需要花费很多时间来设计,设计出现问题,代码实现这一环节自然就会跟着出问题,最后不断更改也只会越改越乱。
    •   之后的题目难度也会不断上升,无论是从题目的复杂度还是代码量都是如此,今后还是应该注重这些方面,在线上、线下课堂、辅导书上多研究。

热门相关:无量真仙   惊世毒妃:轻狂大小姐   学霸女神超给力   夫人你马甲又掉了   名门天后:重生国民千金