OOP PTA题目集4-6总结性BLOG

Posted tyj5417

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OOP PTA题目集4-6总结性BLOG相关的知识,希望对你有一定的参考价值。

一、前言

题目集总结:

题目集4: 一共7道编程题,题量适中难度也适中(除了第1题,题目描述过于繁琐复杂以至于无从下手),考察的内容从基础语法和基础算法的运用到部分高级容器的使用(set等)和Java中部分库类的使用(LocalDate等)再到类的设计以及类的封装性的运用,由浅入深,循序渐进。
题目集5: 一共6道编程题,题量依然不是很多,难度不算大,这次考察的内容较为单一,一个是正则表达式的运用,另一个则是类的设计中聚合关系的运用,让我们见识到了同一个题目,同一批类,却有不同的设计理念使得类之间的组合关系并不相同。
题目集6: 仅仅只有1道编程题,但难度却是直线上升,与前面的题目完全不在一个量级,其考察的是复杂类的设计,“题目如此多娇,引无数英雄竞折腰”,这道题的描述加要求的字数就已经达到了2600+,这致使很多同学望而却步。

题目集感悟:

1) 通过这些编程题的练习,我深刻地认识到编程能力的提高需要不断地练习和学习。从基础语法和算法的应用到高级容器和Java库类的使用,再到类的设计和封装性的运用,这些题目涵盖了编程的多个方面,每个练习都让我从中学到了新的知识和技能。
在练习过程中,我也意识到了编程中的一些重要概念,如封装、聚合关系等。对于类的设计和封装性的运用,我也开始慢慢理解和掌握。同时,我也发现了自己在某些方面的不足和需要进一步提高的地方,比如对于复杂类的设计,我需要更加深入地了解和掌握相关知识。
除了学习和练习,我还需要不断地思考和总结,将所学所练习到的知识和技能整合起来,形成自己的思维模型和编程风格。通过不断地思考和总结,我相信我的编程能力将会得到进一步的提高。
2) 通过这次3个题目集,我终于能够理解老师所说的培养我们的坚韧不拔的品质是什么意思了,题目集6中唯一的1道编程题是题目集4中第1题迭代了2个版本之后的产品,然而我对于题目集4的第1题无从下手,却在题目集6中侥幸得到了96分,事实证明,审题时切忌浮躁,正因如此我才能把题目所给的有价值的信息全部Get到。
3) 不过还是得吐槽一下,题目难度梯度跨度太大,许多同学包括我在内都不是很适应,如果是从难度1一直做到现在的难度5或许同学们的成绩也就不会这么难看了吧。

二、设计和分析

OOP训练集04

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:3
如果下单时间不在营业范围内,输出"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+英文空格+桌号+“:”+英文空格+当前桌的总价
本次题目不考虑其他错误情况,如:桌号、菜单订单顺序颠倒、不符合格式的输入、序号重复等,在本系列的后续作业中会做要求。

输入样例:

在这里给出一组输入。例如:

麻婆豆腐 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:

在这里给出一组输入。例如:

麻婆豆腐 12
油淋生菜 9
table 1 2023/3/22 16/59/59
1 麻婆豆腐 2 2
2 油淋生菜 1 3
1 delete
end

输出样例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

解释和心得:

心得:

第一次见到这道题的时候我和大多数人的想法一样,题目太长,要求太多,设计太复杂,以至于望而却步,不想写,觉得自己写不出来,再看看其他同学,基本和我的情况一致,也就让我放心地放弃这道题,可是当我写完后面那道第5代的题目再回头看看这道题时,内心既佩服之前被懒惰和困难打败的自己又佩服后来坚韧不拔和迎难而上的自己。介于这道题我并没有写,但又写了它的迭代版本,所以我将在后面的第5代版本的题解中再详细介绍做法与思路。

OOP训练集05

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

源代码:

import java.util.Scanner;

public class Main 
    public static void main(String[] args) 
        Scanner input = new Scanner(System.in);
        int year = 0;
        int month = 0;
        int day = 0;

        int choice = input.nextInt();

        if (choice == 1)  // test getNextNDays method
            int m = 0;
            year = Integer.parseInt(input.next());
            month = Integer.parseInt(input.next());
            day = Integer.parseInt(input.next());

            DateUtil date = new DateUtil(day, month, year);

            if (!date.checkInputValidity()) 
                System.out.println("Wrong Format");
                System.exit(0);
            

            m = input.nextInt();

            if (m < 0) 
                System.out.println("Wrong Format");
                System.exit(0);
            
            System.out.println(date.getNextNDays(m).showDate());
         else if (choice == 2)  // test getPreviousNDays method
            int n = 0;
            year = Integer.parseInt(input.next());
            month = Integer.parseInt(input.next());
            day = Integer.parseInt(input.next());

            DateUtil date = new DateUtil(day, month, year);

            if (!date.checkInputValidity()) 
                System.out.println("Wrong Format");
                System.exit(0);
            

            n = input.nextInt();

            if (n < 0) 
                System.out.println("Wrong Format");
                System.exit(0);
            
            System.out.println(date.getPreviousNDays(n).showDate());
         else if (choice == 3)     //test getDaysofDates method
            year = Integer.parseInt(input.next());
            month = Integer.parseInt(input.next());
            day = Integer.parseInt(input.next());

            int anotherYear = Integer.parseInt(input.next());
            int anotherMonth = Integer.parseInt(input.next());
            int anotherDay = Integer.parseInt(input.next());

            DateUtil fromDate = new DateUtil(day, month, year);
            DateUtil toDate = new DateUtil(anotherDay, anotherMonth, anotherYear);

            if (fromDate.checkInputValidity() && toDate.checkInputValidity()) 
                System.out.println(fromDate.getDaysofDates(toDate));
             else 
                System.out.println("Wrong Format");
                System.exit(0);
            
        
        else
            System.out.println("Wrong Format");
            System.exit(0);
        
    


class Year 
    private int value;
    //默认构造函数
    Year() 
    

    //有参构造函数
    Year(int value) 
        this.value = value;
    

    //获取value值的方法
    public int getValue() 
        return value;
    

    //设置value值的方法
    public void setValue(int value) 
        this.value = value;
    

    //判断该年份是否为闰年
    public boolean isLeapYear() 
        if(value % 400 == 0 || (value % 4 == 0 && value % 100 != 0)) return true;
        return false;
    

    //判断该年份是否在合法范围内
    public boolean validate() 
        return value >= 1900 && value <= 2050;
    

    //年份增加1
    public void yearIncrement() 
        value = value + 1;
    

    //年份减少1
    public void yearReduction() 
        value = value - 1;
    



class Month 
    private int value;
    private Year year;
    Month() 

    
    Month(int yearValue, int monthValue) 
        value = monthValue;
        year = new Year(yearValue);
    

    public void setValue(int value) 
        this.value = value;
    

    public int getValue() 
        return value;
    

    public Year getYear() 
        return year;
    

    public void setYear(Year year) 
        this.year = year;
    
    public void resetMin() 
        value = 1;
    
    public void resetMax() 
        value = 12;
    
    public boolean validate() 
        return value >= 1 && value <= 12;
    
    void monthIncrement() 
        if(value == 12) 
            value = 1;
            year.yearIncrement();
         else value += 1;
    
    void monthReduction() 
        if(value == 1) 
            value = 12;
            year.yearReduction();
        
        else value -= 1;
    


class Day 
    private int value;
    private Month month;
    private int[] mon_maxnum = 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31;
    Day() 

    
    Day(int yearValue, int monthValue, int dayValue) 
        value = dayValue;
        month = new Month(yearValue,monthValue);
    

    public int getValue() 
        return value;
    

    public void setValue(int value) 
        this.value = value;
    

    public Month getMonth() 
        return month;
    

    public void setMonth(Month month) 
        this.month = month;
    
    public void resetMin() 
        value = 1;
    
    public void resetMax() 
        if(month.getYear().isLeapYear() && month.getValue() == 2) value = 29;
        else value = mon_maxnum[month.getValue()];
    
    public boolean validate() 
        if(month.getYear().isLeapYear() && month.getValue() == 2) 
            return value <= 29 && value >= 1;
         else 
            if(month.validate()) return value <= mon_maxnum[month.getValue()] && value >= 1;
            else return false;
        
    
    public void dayIncrement() 
        if(month.getYear().isLeapYear() && month.getValue() == 2) 
            if(value == 28) value = 29;
            else if(value == 29) 
                value = 1;
                month.monthIncrement();
             else value += 1;
         else if(value == mon_maxnum[month.getValue()]) 
            value = 1;
            month.monthIncrement();
         else value += 1;
    
    public void dayReduction() 
        value -= 1;
        if(value < 1) 
            if(month.getYear().isLeapYear() && month.getValue() == 3) 
                value = 29;
                month.setValue(2);
             else 
                month.monthReduction();
                value = mon_maxnum[month.getValue()];
            
        
    

class DateUtil 
    private Day day;
    DateUtil() 

    
    DateUtil(int d, int m, int y) 
        day = new Day(y,m,d);
    

    public Day getDay() 
        return day;
    

    public void setDay(Day day) 
        this.day = day;
    
    public boolean checkInputValidity() 
        return this.day.validate() && this.day.getMonth().validate() && this.day.getMonth().getYear().validate();
    
    public boolean compareDates(DateUtil date) 
        if(this.day.getMonth().getYear().getValue() < date.day.getMonth().getYear().getValue()) return true;
        else if(this.day.getMonth().getYear().getValue() == date.day.getMonth().getYear().getValue()) 
            if(this.day.getMonth().getValue() < date.day.getMonth().getValue()) return true;
            else if(this.day.getMonth().getValue() == date.day.getMonth().getValue()) 
                if(this.day.getValue() < date.day.getValue()) return true;
            
         return false;
    
    public boolean equalTwoDates(DateUtil date) 
        return this.day.getMonth().getYear().getValue() == date.day.getMonth().getYear().getValue() &&
                this.day.getMonth().getValue() == date.day.getMonth().getValue() &&
                this.day.getValue() == date.day.getValue();
    
    public String showDate() 
        return day.getMonth().getYear().getValue() + "-" + day.getMonth().getValue() + "-" + day.getValue();
    
    public DateUtil getNextNDays(int n) 
        for (int i = 0; i < n; i++) 
            this.day.dayIncrement();
        
        return this;
    
    public DateUtil getPreviousNDays(int n) 
        for (int i = 0; i < n; i++) 
            this.day.dayReduction();
        
        return this;
    
    public int getDaysofDates(DateUtil date) 
        if(this.equalTwoDates(date)) return 0;
        else 
            int n = 0;
            if(this.compareDates(date)) 
                while (true) 
                   this.getNextNDays(1);
                    n ++;
                    if(this.equalTwoDates(date)) return n;
                
             else 
                while (true) 
                    this.getPreviousNDays(1);
                    n ++;
                    if(this.equalTwoDates(date)) return n;
                
            
        
    

代码质量的分析报告:

1.可读性
这段代码相对来说可读性较好。变量名清晰易懂,函数名也比较符合命名规范。代码结构清晰,缩进合理。但是,在类内部的函数定义中,函数之间缺少空行,不利于代码阅读。代码注释较少,对函数和算法的解释不够详细,需要进一步完善。
2.可维护性
这段代码的可维护性比较好,因为代码结构清晰,变量和函数命名规范。但是,由于函数之间没有空行,代码阅读会有些困难,需要加上空行。另外,代码中存在重复的部分,如输入格式检查部分的代码,可以抽象成一个函数或一个工具类来实现。
3.可测试性
这段代码的可测试性较好,因为它的主要逻辑是基于一些简单的计算和条件判断,这些逻辑都比较容易进行测试。另外,代码中的函数较小,可以单独测试。
4.性能
这段代码的性能一般,因为它没有明显的性能问题,但是它没有对性能进行优化。对于一个简单的日期计算程序来说,性能要求不高,所以这段代码的性能可以接受。
5.可靠性
这段代码的可靠性一般,因为它的输入检查部分有些简单,无法覆盖所有异常情况。在输入不合法的情况下,程序的异常处理部分也不够完善。这些问题可能导致程序出现异常行为,需要进一步完善。
6.安全性
这段代码的安全性一般。它没有直接处理敏感数据,因此不会涉及到安全问题。但是,输入检查不够严格,可能会存在一些潜在的安全问题,需要进一步改进。
7.可扩展性
这段代码的可扩展性较好。它的基本结构比较清晰,函数也比较独立,因此可以比较容易地对其进行扩展。例如,可以增加一个新的日期计算功能,或者修改日期计算逻辑。但是,在代码的结构设计上,需要进一步优化,提高代码的灵活性。

类图:

解释和心得:

代码解释:

包含三个类:Year、Month和Day,以及一个辅助类DateUtil。这些类共同定义了日期对象,可以对日期对象进行一些基本操作,如验证日期是否合法、比较日期、增加/减少日期等。
Year类表示年份,包含一个私有变量value,表示年份的值。类中定义了默认构造函数和一个带参构造函数,分别对value变量进行了初始化。getValue和setValue方法分别用于获取和设置value的值。isLeapYear方法用于判断该年份是否为闰年,如果是闰年则返回true,否则返回false。validate方法用于判断该年份是否在合法范围内(1900年至2050年之间),如果是则返回true,否则返回false。yearIncrement和yearReduction方法分别用于将年份加1和减1。
Month类表示月份,包含两个私有变量value和year,分别表示月份的值和所属的年份。类中定义了默认构造函数和一个带参构造函数,分别对value和year变量进行了初始化。setValue和getValue方法分别用于获取和设置value的值。getYear和setYear方法分别用于获取和设置year的值。resetMin和resetMax方法分别将value的值重置为1和12。validate方法用于判断该月份是否合法(即在1至12之间),如果是则返回true,否则返回false。monthIncrement和monthReduction方法分别用于将月份加1和减1,当value等于12时需要将年份增加1,当value等于1时需要将年份减少1。
Day类表示日期,包含三个私有变量value、month和mon_maxnum,分别表示日期的值、所属的月份和每个月的最大天数。类中定义了默认构造函数和一个带参构造函数,分别对value和month变量进行了初始化。getValue和setValue方法分别用于获取和设置value的值。getMonth和setMonth方法分别用于获取和设置month的值。resetMin和resetMax方法分别将value的值重置为1和每个月的最大天数,如果该月份是闰年的2月,则最大天数为29。validate方法用于判断该日期是否合法,首先需要判断该月份是否合法,如果不合法则直接返回false;如果该月份是闰年的2月,则需要特殊判断该日期是否合法;否则根据该月份的最大天数进行判断。dayIncrement和dayReduction方法分别用于将日期加1和减1,当value等于每个月的最大天数时需要将月份加1,当value等于1时需要将月份减1,如果该月份是闰年的2月,则需要特殊处理。
DateUtil类是整个日期工具类的管理类,主要用于进行日期操作,包括日期初始化、日期比较、日期合法性校验等操作。其中checkInputValidity方法用于检查日期的合法性,compareDates方法用于比较两个日期的大小,equalTwoDates方法用于判断两个日期是否相等。这些方法都是基于Year、Month和Day类提供的方法进行实现的。

心得:

这个题目并没有涉及很高深的算法实现,只是根据老师给的类图完成类的设计,难度不大。
通过这个类图的设计,可以看出良好的面向对象设计理念,将不同的功能分别封装在不同的类中,并通过类之间的相互调用实现了整个日期操作的功能。同时,类中的属性和方法都进行了合理的封装,使得整个程序更加安全和易于维护。
在实际的开发过程中,我们可以根据这个类图的设计思路,借鉴其中的一些设计思想,从而提高程序的可扩展性和可维护性。例如,在开发一个日期操作模块时,可以参考这个类图中的设计思路,将日期的各个部分封装在不同的类中,并提供相应的属性和方法进行操作,同时通过一个管理类进行统一的调用。这样可以有效地避免代码重复和混乱,提高代码的可读性和可维护性。

OOP训练集05

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 are:值

输入样例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

源代码:

import java.util.Scanner;

public class Main 
    public static void main(String[] args) 
        Scanner input = new Scanner(System.in);
        int year = 0;
        int month = 0;
        int day = 0;

        int choice = input.nextInt();

        if (choice == 1)  // test getNextNDays method
            int m = 0;
            year = Integer.parseInt(input.next());
            month = Integer.parseInt(input.next());
            day = Integer.parseInt(input.next());

            DateUtil date = new DateUtil(year, month, day);

            if (!date.checkInputValidity()) 
                System.out.println("Wrong Format");
                System.exit(0);
            

            m = input.nextInt();

            if (m < 0) 
                System.out.println("Wrong Format");
                System.exit(0);
            

            System.out.print(date.getYear().getValue() + "-" + date.getMonth().getValue() + "-" + date.getDay().getValue() + " next " + m + " days is:");
            System.out.println(date.getNextNDays(m).showDate());
         else if (choice == 2)  // test getPreviousNDays method
            int n = 0;
            year = Integer.parseInt(input.next());
            month = Integer.parseInt(input.next());
            day = Integer.parseInt(input.next());

            DateUtil date = new DateUtil(year, month, day);

            if (!date.checkInputValidity()) 
                System.out.println("Wrong Format");
                System.exit(0);
            

            n = input.nextInt();

            if (n < 0) 
                System.out.println("Wrong Format");
                System.exit(0);
            

            System.out.print(
                    date.getYear().getValue() + "-" + date.getMonth().getValue() + "-" + date.getDay().getValue() + " previous " + n + " days is:");
            System.out.println(date.getPreviousNDays(n).showDate());
         else if (choice == 3)     //test getDaysofDates method
            year = Integer.parseInt(input.next());
            month = Integer.parseInt(input.next());
            day = Integer.parseInt(input.next());

            int anotherYear = Integer.parseInt(input.next());
            int anotherMonth = Integer.parseInt(input.next());
            int anotherDay = Integer.parseInt(input.next());

            DateUtil fromDate = new DateUtil(year, month, day);
            DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay);

            if (fromDate.checkInputValidity() && toDate.checkInputValidity()) 
                System.out.println("The days between " + fromDate.showDate() +
                        " and " + toDate.showDate() + " are:"
                        + fromDate.getDaysofDates(toDate));
             else 
                System.out.println("Wrong Format");
                System.exit(0);
            
        
        else
            System.out.println("Wrong Format");
            System.exit(0);
        
    


class Year 
    private int value;
    //默认构造函数
    Year() 
    

    //有参构造函数
    Year(int value) 
        this.value = value;
    

    //获取value值的方法
    public int getValue() 
        return value;
    

    //设置value值的方法
    public void setValue(int value) 
        this.value = value;
    

    //判断该年份是否为闰年
    public boolean isLeapYear() 
        if(value % 400 == 0 || (value % 4 == 0 && value % 100 != 0)) return true;
        return false;
    

    //判断该年份是否在合法范围内
    public boolean validate() 
        return value >= 1820 && value <= 2020;
    

    //年份增加1
    public void yearIncrement() 
        value += 1;
    

    //年份减少1
    public void yearReduction() 
        value -= 1;
    


class Month 
    private int value;
    Month() 

    
    Month(int value) 
        this.value = value;
    

    public void setValue(int value) 
        this.value = value;
    

    public int getValue() 
        return value;
    
    public void resetMin() 
        value = 1;
    
    public void resetMax() 
        value = 12;
    
    public boolean validate() 
        return value >= 1 && value <= 12;
    
    void monthIncrement() 
        value += 1;
    
    void monthReduction() 
        value -= 1;
    


class Day 
    private int value;
    Day() 

    
    Day( int value) 
        this.value = value;
    

    public int getValue() 
        return value;
    

    public void setValue(int value) 
        this.value = value;
    
    public void dayIncrement() 
        value += 1;
    
    public void dayReduction() 
        value -= 1;
    


class DateUtil 
    private Year year = new Year();
    private Month month = new Month();
    private Day day = new Day();
    private int[] mon_maxnum = 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31;
    DateUtil() 

    
    DateUtil(int y, int m, int d) 
        year.setValue(y);
        month.setValue(m);
        day.setValue(d);
    
    public Year getYear() 
        return year;
    
    public void setYear(Year year) 
        this.year = year;
    
    public Month getMonth() 
        return month;
    
    public void setMonth(Month month) 
        this.month = month;
    
    public Day getDay() 
        return day;
    
    public void setDay(Day day) 
        this.day = day;
    
    public void setDayMin() 
        day.setValue(1);
    
    public void setDayMax() 
        day.setValue(mon_maxnum[month.getValue()]);
    
    public boolean checkInputValidity() 
        boolean validateDay;
        if(year.isLeapYear() && month.getValue() == 2) validateDay = day.getValue() >= 1 && day.getValue() <= 29;
        else validateDay =  day.getValue() >= 1 && day.getValue() <= mon_maxnum[month.getValue()];
        return validateDay && year.validate() && month.validate();
    
    public boolean compareDates(DateUtil date) 
        if(this.year.getValue() < date.year.getValue()) return true;
        else if(this.year.getValue() == date.year.getValue()) 
            if(this.month.getValue() < date.month.getValue()) return true;
            else if(this.month.getValue() == date.month.getValue()) 
                return this.day.getValue() < date.day.getValue();
            
         return false;
    
    public boolean equalTwoDates(DateUtil date) 
        return this.year.getValue() == date.year.getValue() &&
                this.month.getValue() == date.month.getValue() &&
                this.day.getValue() == date.day.getValue();
    
    public String showDate() 
        return year.getValue() + "-" + month.getValue() + "-" + day.getValue();
    
    public DateUtil getNextNDays(int n) 
        for (int i = 0; i < n; i++) 
            if(year.isLeapYear()) mon_maxnum[2] = 29;
            else mon_maxnum[2] = 28;
            if(day.getValue() + 1 > mon_maxnum[month.getValue()]) 
                day.setValue(1);
                if(month.getValue() + 1 > 12) 
                    month.resetMin();
                    year.yearIncrement();
                 else month.monthIncrement();
             else day.dayIncrement();
        
        return this;
    
    public DateUtil getPreviousNDays(int n) 
        for (int i = 0; i < n; i++) 
            if(year.isLeapYear()) mon_maxnum[2] = 29;
            else mon_maxnum[2] = 28;
            if(day.getValue() - 1 == 0) 
                if(month.getValue() - 1 == 0) 
                    month.resetMax();
                    year.yearReduction();
                    day.setValue(mon_maxnum[month.getValue()]);
                 else 
                    month.monthReduction();
                    day.setValue(mon_maxnum[month.getValue()]);
                
             else day.dayReduction();
        
        return this;
    
    public int getDaysofDates(DateUtil date) 
        if(this.equalTwoDates(date)) return 0;
        else 
            int n = 0;
            if(this.compareDates(date)) 
                while (true) 
                    this.getNextNDays(1);
                    n ++;
                    if(this.equalTwoDates(date)) return n;
                
             else 
                while (true) 
                    this.getPreviousNDays(1);
                    n ++;
                    if(this.equalTwoDates(date)) return n;
                
            
        
    

代码质量的分析报告:

1.可读性
代码结构清晰,类和方法命名符合命名规范,注释也比较详细,容易理解代码的功能和作用。但是,有一些命名不够具有描述性,例如 Month 和 Day 类中的 setValue 和 getValue 方法,可以考虑修改为 setMonth 和 getMonth 等。
2.可维护性
代码的可维护性良好,因为代码结构清晰,命名规范,注释详细,易于理解和修改。每个类都有默认构造函数和有参构造函数,使得可以方便地创建对象,修改属性值。代码中还使用了常量,如月份天数,增加了代码的可读性,也方便修改。
3.可测试性
代码的可测试性良好。每个类都只包含单一的职责,各方法之间耦合度较低,易于编写单元测试和集成测试。此外,代码中包含了一些判断是否合法、是否闰年等方法,这些方法可以方便地单独测试。
4.性能
这段代码的性能问题不大。因为代码中没有使用到复杂的算法,所以时间复杂度较低,不会对系统性能产生显著的影响。但是 getNextNDays 和 getPreviousNDays 方法中使用了 for 循环,如果 n 很大,可能会造成时间和空间的浪费。
5.可靠性
代码中没有明显的逻辑错误或编译错误,所以代码的可靠性较高。但是,由于没有做足够的异常处理,例如当输入的日期不合法时,会引发异常,需要加入异常处理机制。
6.安全性
从代码本身来看,没有明显的安全问题。但是,如果代码涉及到网络或文件等操作,需要考虑安全性问题,避免信息泄漏等风险。
7.可扩展性
代码中的 Year、Month 和 Day 类具有较好的可扩展性,可以添加新的方法和属性,方便扩展新的功能。但是,DateUtil 类依赖于这三个类,如果需要扩展 DateUtil 的功能,需要先对这三个类进行修改,可能会影响到其他模块的使用。因此,可以考虑使用接口来降低耦合度,提高代码的可扩展性。

类图:

解释和心得:

代码解释:

首先定义了一个Year类,包括一个私有变量value和构造函数,value表示年份,构造函数有默认构造函数和有参构造函数,提供了获取和设置value值的方法,判断是否为闰年的方法,判断年份是否在合法范围内的方法,以及年份增减的方法。
其次定义了一个Month类,包括一个私有变量value和构造函数,value表示月份,构造函数有默认构造函数和有参构造函数,提供了获取和设置value值的方法,判断月份是否在合法范围内的方法,以及月份增减的方法。
然后定义了一个Day类,包括一个私有变量value和构造函数,value表示日期,构造函数有默认构造函数和有参构造函数,提供了获取和设置value值的方法,以及日期增减的方法。
最后定义了一个DateUtil类,包括Year、Month和Day类的对象,提供了一些日期计算相关的方法,如获取下一个或上一个n天的日期,判断输入日期是否合法,判断两个日期的大小关系等等。
其中mon_maxnum数组表示每个月份的天数,可以通过setDayMax方法将Day的值设置为当前月份的最大值,通过setDayMin方法将Day的值设置为1。
此外,还提供了一个showDate方法,用于将日期对象以字符串的形式输出。
getNextNDays(int n):将当前日期向后推 n 天,并返回更新后的日期。具体实现是循环执行 n 次,每次判断是否需要进位(例如跨月或跨年),然后更新年月日的值。
getPreviousNDays(int n):将当前日期向前推 n 天,并返回更新后的日期。具体实现与 getNextNDays(int n) 类似,只是方向相反。
getDaysofDates(DateUtil date):计算当前日期和传入日期 date 之间相差的天数。如果两个日期相等返回 0,如果当前日期早于 date,则循环调用 getNextNDays(1) 直到两个日期相等,返回循环的次数,否则循环调用 getPreviousNDays(1) 直到两个日期相等,返回循环的次数。
这些算法实现比较简单,都是基于年月日的加减法和比较逻辑来实现的,没有使用到复杂的算法和数据结构。

心得:

这个题目并没有涉及很高深的算法实现,也只是根据老师给的类图完成类的设计,难度不大。
这些类的设计展现了一个良好的面向对象设计,将日期相关的类和方法进行了合理的分离和封装。DateUtil类作为日期工具类,封装了日期计算的方法,而Year、Month和Day类则提供了基本的日期操作方法,这样的设计使得代码更加简洁和易于维护。
此外,使用成员变量对Year、Month和Day对象进行封装,可以提高代码的封装性和可读性,同时也方便了其他类对日期的操作。而提供的基本日期操作方法也使得日期的操作变得更加简单和直观。

OOP训练集06

7-1 菜单计价程序-4

题目:

本体大部分内容与菜单计价程序-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)//根据序号查找一条记录

本次课题比菜单计价系列-3增加的异常情况:
1、菜谱信息与订单信息混合,应忽略夹在订单信息中的菜谱信息。输出:"invalid dish"
2、桌号所带时间格式合法(格式见输入格式部分说明,其中年必须是4位数字,月、日、时、分、秒可以是1位或2位数),数据非法,比如:2023/15/16 ,输出桌号+" date error"
3、同一桌菜名、份额相同的点菜记录要合并成一条进行计算,否则可能会出现四舍五入的误差。
4、重复删除,重复的删除记录输出"deduplication :"+序号。
5、代点菜时,桌号不存在,输出"Table number :"+被点菜桌号+" does not exist";本次作业不考虑两桌记录时间不匹配的情况。
6、菜谱信息中出现重复的菜品名,以最后一条记录为准。
7、如果有重复的桌号信息,如果两条信息的时间不在同一时间段,(时段的认定:周一到周五的中午或晚上是同一时段,或者周末时间间隔1小时(不含一小时整,精确到秒)以内算统一时段),此时输出结果按不同的记录分别计价。
8、重复的桌号信息如果两条信息的时间在同一时间段,此时输出结果时合并点菜记录统一计价。前提:两个的桌号信息的时间都在有效时间段以内。计算每一桌总价要先合并符合本条件的饭桌的点菜记录,统一计价输出。
9、份额超出范围(1、2、3)输出:序号+" portion out of range "+份额,份额不能超过1位,否则为非法格式,参照第13条输出。
10、份数超出范围,每桌不超过15份,超出范围输出:序号+" num out of range "+份数。份数必须为数值,最高位不能为0,否则按非法格式参照第16条输出。
11、桌号超出范围[1,55]。输出:桌号 +" table num out of range",桌号必须为1位或多位数值,最高位不能为0,否则按非法格式参照第16条输出。
12、菜谱信息中菜价超出范围(区间(0,300)),输出:菜品名+" price out of range "+价格,菜价必须为数值,最高位不能为0,否则按非法格式参照第16条输出。
13、时间输入有效但超出范围[2022.1.1-2023.12.31],输出:"not a valid time period"
14、一条点菜记录中若格式正确,但数据出现问题,如:菜名不存在、份额超出范围、份数超出范围,按记录中从左到右的次序优先级由高到低,输出时只提示优先级最高的那个错误。
15、每桌的点菜记录的序号必须按从小到大的顺序排列(可以不连续,也可以不从1开始),未按序排列序号的输出:"record serial number sequence error"。当前记录忽略。(代点菜信息的序号除外)
16、所有记录其它非法格式输入,统一输出"wrong format"
17、如果记录以“table”开头,对应记录的格式或者数据不符合桌号的要求,那一桌下面定义的所有信息无论正确或错误均忽略,不做处理。如果记录不是以“table”开头,比如“tab le 55 2023/3/2 12/00/00”,该条记录认为是错误记录,后面所有的信息并入上一桌一起计算。
次作业比菜单计价系列-3增加的功能:
菜单输入时增加特色菜,特色菜的输入格式:菜品名+英文空格+基础价格+"T"
例如:麻婆豆腐 9 T
菜价的计算方法:
周一至周五 7折, 周末全价。
注意: 不同的四舍五入顺序可能会造成误差,请按以下步骤累计一桌菜的菜价:
计算每条记录的菜价:将每份菜的单价按份额进行四舍五入运算后,乘以份数计算多份的价格,然后乘以折扣,再进行四舍五入,得到本条记录的最终支付价格。
最后将所有记录的菜价累加得到整桌菜的价格。

输入格式:

桌号标识格式: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+英文空格+桌号+“:”+英文空格+当前桌的原始总价+英文空格+当前桌的计算折扣后总价

源代码:


import java.time.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main 
    public static void main(String[] args) 
        Map<String, Integer> dishOutput = new HashMap<>();
        List<Map<String, Integer>> mapList = new ArrayList<>();
        Map<Integer, List<Map<String, Integer>>> tableDishOutput = new HashMap<>();
        int numOutput = -1;
        boolean is_equals = false;
        List<List<Integer>> ordersDeletes = new ArrayList<>(); // 记录每个order的订单删除记录
        List<String> outPuts = new ArrayList<>();
        Menu menu = new Menu();
        List<Order> orders = new ArrayList<>();
        int n = -1; // 记录order的个数,一旦n != 1后,后续则不为菜谱信息
        int unit_price = 0;
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        boolean tableFlag = true; // 用来判断桌子信息是否有误
        boolean menuFlag = true;
        while (!input.equals("end")) 
            String[] strings = input.split("\\\\s+");
            if (strings.length == 2 && !strings[1].equals("delete") && n == -1 && menuFlag)  // 判断为普通菜
                String pattern = "\\\\S+ \\\\d+";
                Pattern p = Pattern.compile(pattern);
                Matcher m = p.matcher(input);
                if(!m.matches()) 
                    numOutput ++;
                    outPuts.add("wrong format");
                
                else 
                    char[] chars = input.toCharArray();
                    int Numblank = 0;
                    for (int i = 0; i < input.length(); i ++) 
                        if(chars[i] == \' \') Numblank ++;
                    
                    if(Numblank > 1) 
                        numOutput ++;
                        outPuts.add("wrong format");
                    
                    else 
                        char[] arr = strings[1].toCharArray();
                        if(!isInteger(strings[1]) || arr[0] == \'0\' && strings[1].length() > 1 || isInteger(strings[0])) 
                            numOutput ++;
                            outPuts.add("wrong format");
                        
                        else 
                            unit_price = Integer.parseInt(strings[1]);
                            if(unit_price > 300 || unit_price <= 0) 
                                numOutput ++;
                                outPuts.add(strings[0] + " price out of range " + unit_price);
                            
                            else 
                                if(menu.searthDish(strings[0]) == null)
                                    menu.addDish(strings[0], unit_price, \'C\');
                                else menu.modDishs(strings[0], unit_price, \'C\');
                            
                        
                    
                
             else if(strings.length == 3 && n == -1 && menuFlag)  // 判断为特色菜
                String pattern = "\\\\S+ \\\\d+ T";
                Pattern p = Pattern.compile(pattern);
                Matcher m = p.matcher(input);
                if(!m.matches()) 
                    numOutput ++;
                    outPuts.add("wrong format");
                
                else 
                    char[] chars = input.toCharArray();
                    int Numblank = 0;
                    for (int i = 0; i < input.length(); i ++) 
                        if(chars[i] == \' \') Numblank ++;
                    
                    if(Numblank > 2) 
                        numOutput ++;
                        outPuts.add("wrong format");
                    
                    else 
                        char[] arr = strings[1].toCharArray();
                        if(!isInteger(strings[1]) || arr[0] == \'0\' && strings[1].length() > 1 || isInteger(strings[0])) 
                            numOutput ++;
                            outPuts.add("wrong format");
                        
                        else 
                            unit_price = Integer.parseInt(strings[1]);
                            if(unit_price >= 300 || unit_price <= 0) 
                                numOutput ++;
                                outPuts.add(strings[0] + " price out of range " + unit_price);
                            
                            else 
                                if(menu.searthDish(strings[0]) == null) // 判断是否有重复的菜品信息
                                    menu.addDish(strings[0], unit_price, \'T\');
                                else menu.modDishs(strings[0], unit_price, \'T\');
                            
                        
                    
                
             else if(strings[0].equals("table"))  // 判断为桌子信息
                is_equals = false;
                String pattern = "table \\\\d1,2 \\\\d4/\\\\d1,2/\\\\d1,2 \\\\d1,2/\\\\d1,2/\\\\d1,2";
                Pattern p = Pattern.compile(pattern);
                Matcher m = p.matcher(input);
                if(!m.matches()) 
                    numOutput ++;
                    outPuts.add("wrong format");
                    tableFlag = false;
                 else 
                    menuFlag = false;
                    if(strings.length != 4) 
                        numOutput ++;
                        outPuts.add("wrong format");
                        tableFlag = false;
                    
                    else 
                        LocalDate localDate = null;
                        LocalTime localTime = null;
                        int tableNum;
                        char[] arr = strings[1].toCharArray();
                        if(arr[0] == \'0\') 
                            numOutput ++;
                            outPuts.add("wrong format");
                            tableFlag = false;
                        
                        else 
                            int dateN = 0, timeN = 0;
                            char[] charsDate = strings[2].toCharArray();
                            char[] charsTime = strings[3].toCharArray();
                            for (int i = 0; i < strings[2].length(); i++) 
                                if(charsDate[i] == \'/\') dateN ++;
                            
                            for (int i = 0; i < strings[3].length(); i++) 
                                if(charsTime[i] == \'/\') timeN ++;
                            
                            if(timeN != 2 || dateN != 2) 
                                numOutput ++;
                                outPuts.add("wrong format");
                                tableFlag = false;
                             else 
                                String[] date = strings[2].split("/+");
                                String[] time = strings[3].split("/+");
                                if(!isInteger(strings[1]) || // 判断日期格式
                                        !isInteger(date[0]) ||
                                        !isInteger(date[1]) ||
                                        !isInteger(date[2]) ||
                                        !isInteger(time[0]) ||
                                        !isInteger(time[1]) ||
                                        !isInteger(time[2]) ||
                                        date[0].length() != 4 ||
                                        date[1].length() > 2 ||
                                        date[2].length() > 2 ||
                                        time[0].length() > 2 ||
                                        time[1].length() > 2 ||
                                        time[2].length() > 2) 
                                    numOutput ++;
                                    outPuts.add("wrong format");
                                    tableFlag = false;
                                
                                else 
                                    tableNum = Integer.parseInt(strings[1]);
                                    if(tableNum > 55 || tableNum < 1)  // 判断桌号是否满足题意
                                        numOutput ++;
                                        outPuts.add(tableNum + " table num out of range");
                                        tableFlag = false;
                                    
                                    else 
                                        boolean flag = false;
                                        try 
                                            int year = Integer.parseInt(date[0]);
                                            int month = Integer.parseInt(date[1]);
                                            int day = Integer.parseInt(date[2]);
                                            int hour = Integer.parseInt(time[0]);
                                            int minute = Integer.parseInt(time[1]);
                                            int second = Integer.parseInt(time[2]);
                                            localDate = LocalDate.of(year, month, day);
                                            localTime = LocalTime.of(hour, minute, second);
                                            flag = true;
                                         catch (DateTimeException e) 
                                            numOutput 

PTA题目集7-9的总结

PTA题目集7-9的总结

 —————————————————————————————————————————————————————————————————————————

目录:

    1. 前言

 

    2. 设计与分析

 

    3. 采坑心得

 

    4. 总结

 

前言:

 

  关于题目集7:

 题目集四主要是关于java的多态以及继承的知识点,两道题目都比较简单,考察的新知识点为比较器的使用,利用比较器我能更好更方便的使用列表,节省了我很多时间。

 

  关于题目集8:

 题目集八只有一道题目,考察的是ATM机类结构的设计,题目中给了很多的信息,包括各个类的类图以及要输入的数据,要把这些类的关系搞清楚花了我不少时间,并且输入数据也比较繁琐,主要的难点还是搞清楚类的关系,知识点都是之前考察过的。

 

  关于题目集9:

题目集九是题目集八的延伸,在题目集八的基础上加入了贷款的功能,并且加入了另外几个人的信息,和题目集八差不多,并不是那么难。

 

——————————————————————————————————————————————————————————————————————————

 

设计与分析:

 

    这块主要对一些题目和源码进行一些分析。

 

关于题目集七的7-1与7-2:

 

  两道题目都考察了继承与多态的知识

 

  7-1 图形卡片排序游戏

输入格式:

  • 首先,在一行上输入一串数字(1~4,整数),其中,1代表圆形卡片,2代表矩形卡片,3代表三角形卡片,4代表梯形卡片。各数字之间以一个或多个空格分隔,以“0”结束。例如: 1 3 4 2 1 3 4 2 1 3 0
  • 然后根据第一行数字所代表的卡片图形类型,依次输入各图形的相关参数,例如:圆形卡片需要输入圆的半径,矩形卡片需要输入矩形的宽和长,三角形卡片需要输入三角形的三条边长,梯形需要输入梯形的上底、下底以及高。各数据之间用一个或多个空格分隔。

输出格式:

  • 如果图形数量非法(小于0)或图形属性值非法(数值小于0以及三角形三边不能组成三角形),则输出Wrong Format
  • 如果输入合法,则正常输出,所有数值计算后均保留小数点后两位即可。输出内容如下:
  1. 排序前的各图形类型及面积,格式为图形名称1:面积值1图形名称2:面积值2 …图形名称n:面积值n ,注意,各图形输出之间用空格分开,且输出最后存在一个用于分隔的空格;
  2. 排序后的各图形类型及面积,格式同排序前的输出;
  3. 所有图形的面积总和,格式为Sum of area:总面积值

 7-1与7-2差不多,这里先分析7-1,7-1这里主要先使用列表存储输入的数据,再对列表进行排序最后输出,这里先附上类图。

这道题涉及的类比较少,所以比较简单,关键在于比较器的使用。

public int compareTo(Shape s){
        if(getArea()>s.getArea())
            return -1;
        else if (getArea()<s.getArea())
            return 1;
        else 
            return 0;
    }

每个图形类都包含了这个方法,在对列表排序的时候就使用了这个方法进行排序,非常方便。

关于题目集八与题目集九

题目集八和题目集九都是关于ATM机结构类设计,题目集九多加了一个贷款的功能,并且区分了两种账户。

  7-1 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编号]上取款¥[金额]

当前余额为¥[金额]

其中,[]说明括起来的部分为输出属性或变量,金额均保留两位小数。

③查询余额业务输出

业务:查询余额 ¥[金额]

金额保留两位小数。

附上类图:

 

这道题目主要是搞清楚类之间的关系,以及预想功能的实现,清楚这些之后问题就迎刃而解了。

public void withdraw() {
        /**
         * 校验该卡是否存在
         */        
        Card card = ValidateData.getCardbyCardNO(unionPay, cardNO);
        if(card == null) {
            System.out.println("Sorry,this card does not exist.");
            System.exit(0);
        }
        
        /**
         * 校验ATM是否存在
         */
        ATM aTM = ValidateData.getATMbyATMID(unionPay, ATMID);
        if(aTM == null) {
            System.out.println("Sorry,the ATM\'s id is wrong.");
            System.exit(0);
        }
        
        Account account = Account.getAmountbyCardNO(cardNO);
        double balance = account.getBalance();
        
        /**
         * 校验卡密码是否正确
         */
        if(!card.getCardPassword().equals(cardPassword)) {
            System.out.println("Sorry,your password is wrong.");
            System.exit(0);
        }
        /**
         * 校验是否为跨行取款
         */
        if (account.getBank().getBankNO() != aTM.getBank().getBankNO()) {
            String BankNO = aTM.getBank().getBankNO();
            switch(BankNO){
                case "1001": amount_fact = amount + amount*0.02;
                break;
                case "1002": amount_fact = amount + amount*0.03;
                break;
                case "1003": amount_fact = amount + amount*0.04;
                break;
            }
        }    
        /**
         * 校验取款金额是否大于余额
         */
        if (amount_fact > balance) {
            if(account.getLoan()&&amount_fact+(amount - balance)*0.05-balance<=50000){
                if(balance > 0)
                    amount_fact = amount_fact + (amount - balance)*0.05;
                else
                    amount_fact = amount_fact + amount*0.05;
            }
            else {
                System.out.println("Sorry,your account balance is insufficient.");
                System.exit(0);}
        }
        account.setBalance(balance - amount_fact);//取款更新余额操作

        if(amount >= 0) {
            showResult(account,1);
        }else {
            showResult(account,0);
        }
        
    }

这道题其中一个难点便是卡号的查找了,你需要在每一个银行都找一遍才行,为此需要先遍历存储银行的列表,再去遍历银行里的账户,做完之后输出正确或错误结果即可。

采坑心得

   

关于正则表达式:

 

 在做题目的过程中中运用了正则表达式,而正则表达式算是这次题中的一个难点,我在用正则表达式中遇到的第一个问题便是匹配双引号。在题目中有这样一个要求。

要求中说明了注释和字符串中出现的关键词不计,但在我使用的过程中总是出现从第一个双引号匹配到最后一个双引号的情况,我很长时间才发现问题所在,原来是我的正则表达式中用了/S来进行检索,它把引号也一起检索进去了,换成/w就可解决,究其原因还是我自己考虑的不太充分。

 

总结

 这次的题目集虽然看似比较少,但是题目有一定的难度,在类的关系反面考察的比较多,让我对类的应用有了更深刻的理解,同时也让我对列表的运用有了一定理解,让我学用列表排序存储数据等操作,总之这次的题目集让我收获比较多。

以上是关于OOP PTA题目集4-6总结性BLOG的主要内容,如果未能解决你的问题,请参考以下文章

PTA题目集4~6总结

JAVA面向对象程序设计_PTA题目集04-06总结分析

OOP 4-6次题目集总结

OOP课程题目集第二次总结

题目集4~6的总结性Blog

OOP学习的第二次BLOG