第一次博客作业:PTA题目集1-3总结性Blog

                                    PTA题目集1-3总结性Blog

Java是一门面向对象的编程语言,与面向过程的C语言截然不同,尤其是在代码量大的时候,面向对象与面向过程的差别就尤为突出了。对于刚接触Java的我们来说,要从面向过程的思想转变到面向对象并不是一件简单的事情。

PTA三次作业概述

         对于这三次大作业,我个人认为大致可以分为两种题型:第一种是菜单系列的题目,旨在通过让我们熟悉类和方法的设计与使用,进而充分理解Java的核心思想:即面向对象思维。第二种是排除菜单系列在外的其他题目,每道题都对应着Java的一个或者多个知识点,例如小语法,算法等。

         题量适中,难度自然而然也是在逐渐递增的,但我觉得都在合理范围之内。当然这些是建立在肯于花费时间和精力刻苦学习,对于作业认真完成的前提下。

         知识点:核心还是类的设计与使用,以及方法的实现。其次是各种语法上的知识点和算法。例如if-else选择条件语句的使用,字符串的提取,字符串转整型数字等,此处不在一 一列举,在下文中进行详细阐述。

PTA第一次作业:

   第一次作业主要是为了让我们适应Java的语言环境,作业中并不需要去明白什么是类和对象、属性,什么是方法等,只需要知道Java输入的标准模板,其他基本语法的使用与C语言并无太大区别。 

   难度:简单。                                 

 知识储备

   输入类Scanner的使用;整型、浮点型、字符串型数据的定义与使用;输出类的使用;整型数据与字符串数据间的转化;字符串的各种方法的使用(例如charAT--获取字符,subString--提取子串等)。

 知识点

   7-1 身体质量指数(BMI)测算:if-else选择条件语句的使用。

   7-6 学号识别:字符子串的提取。

   7-7 判断三角形类型: 解决浮点型数据的比较、计算时的误差问题(常用的精度为1e-6,也就是0.000001)。

   7-8 巴比伦法求平方根近似值:Math类的使用--该题中使用整数型数据通过abs方法取绝对值。

   7-9 二进制数值提取:从字符串中获取字符元素--string类中的charAt方法。

PTA第二次作业:

   第二次作业中开始真正步入Java这门语言的殿堂了,开始逐渐揭开面向对象的神秘面纱(但是这个过程真不是so easy的!)。本次作业的题量并不多,只有四题。但是强度完完全全是上来了的,尤其是7-1、7-2的 菜单系列 题目。7-3的 jmu-java-日期类的基本使用 和7-4的 小明走格子 还能接受,但是要AC也不容易。需要的知识储备很多,7-4题目简短,编程出来的代码量也不多,但是很容易卡在运行超时的点,就需要改进算法了。

   当然虽然这次作业强度偏大,但是只要能够静下心来去做,还是能够很好的完成的。正所谓收获与付出成正比,一份耕耘一份收获。通过本次作业,也能够学会很多东西,最值得说道的就是对于类的设计与使用。

   难度:中等偏上。

   当然,老师还是很人性的为我们设计好了大致的模板,包括类和各个类中对应的方法和属性。但是,我想这也避免不了大家对老师的诽腹和牢骚。

 知识储备 

   类的设计与使用;日期类Calendar的基本使用;递归与记忆化搜索(用于7-4的小明走格子问题。当然,这是我个人的方法,一千个读者一千个哈姆雷特,每个人都有自己的思路与做法。总而言之,这题最后的落脚点还是解决运行超时的问题。)

 知识点

   本次作业7-1、7-2与第三次作业7-1的菜单系列题目在最后一起详细阐述。

   7-3 jmu-java-日期类的基本使用:考察对于日期类的基本使用。

   7-4 小明走格子:考察找规律和对于运行超时的解决问题,通常是通过替换算法等优化时间效率。

 设计与分析

   7-3 jmu-java-日期类的基本使用:

   考察对于日期类的基本使用。该题的方法有很多,题目中透露出的提示信息是日期类,相信很多小伙伴都是上CSDN或者B站等地方搜索学习的。那么自然而然就有很多方法可以使用,包括Calendar类,date类等。

   本题AC的难题不是对于这些类的学习与使用,而是我们善良有爱的老师设置的测试点。有一说一啊,这个测试点是真的阴间啊(强烈声明:此处没有言语辱骂之意哈!)。废话不多说,先上测试点

       

 

 

          当我自测通过这五个测试点后,兴高采烈的点击提交,静待显示红色的“答案正确”时,好家伙,没有通过一个测试点,全部显示答案错误。给我人逼疯了,属实找不到坑在哪。但是须知当上帝给你关上一扇门的同时必定会为你打开另一扇窗的,当时鸡王正好在我旁边,于是我直接求教鸡王,一听坑在空格和字母还有负号的非法输入,第一反应就是:亲爱的蔡老师啊,感~恩~~有~你~~,感~谢~~有~你~~。知道坑在哪后还是很好AC这题的。

          以下给出我的过坑代码(非法输入空格时,通过split方法按空格分隔子串,判断子串长度即可)

//判断是否有输入字母或者”-“,存在即判定为不合法
for(int i = 0;i < 3;i++)
{
        for(int j = 0;j < sub_string[i].length();j++)
        {
              if(sub_string[i].charAt(j)<'0'||sub_string[i].charAt(j)>'9'||sub_string[i].charAt(j) == '-')
              {
      return 0;
    }
  }
}

 

   这周三的Java课上学习了正则表达式,做本次作业时并不知道正则表达式的存在,所以在判断格式正常的日期输入是否合法时只能手打,按年按月按日的去逐一判断。通过if-else语句或者switch语句来实现。这也导致了圈复杂度较高。

   以下给出使用正则表达式优化后的日期合法判断方法:

class Judge

{

  boolean flag = true;
  boolean judge_illege(String time)
  {
    flag = "([-9][}[91[0-9[2{}[-91][0-9]}[0-9161][919-9121[910913(1((([5[18101(191][201013[01]))|*+"(146911)1)(01[0912]2101010)                         (2()01091010~1010)((-9]2})(0[48][2468]048][13579]126])[+(181][4161481][35791]3]0)))02\29)$";
    return flag;
  }
}

  

   7-4 小明走格子:

   本题主要考察运行超时和找规律的问题,第一眼看到这个题目反应就是坑可能在运行超时(这种坑在比赛中是最常见的症结所在,但是往往又很难解决,需要通过使用时间复杂度更低的算法来优化时间效率)。

   这题的规律并不难,需要注意到的是当格子数为1、2、3、4时,走法分别有1,2,4,8种。

   规律:① f(n) = f(n-1)+f(n-2)+f(n-3)+f(n-4)

       ② f(n) = 2*f(n-1)-f(n-5)

   注意:若选择第二种方法需要预先设定好当格子数为5的时候,走法为f(5) = 15。

   我用的是递归算法来解决,为了解决运行超时的问题自然而然的我就想到了记忆化搜索的方法。通过记忆化搜索避免避免回归时重复访问,进而优化时间效率。

   以下是递归与记忆化搜索的代码:

public static int function(int m)//计算走法总数
{
  if(m == 1)//只有一个格子的走法总数
    return 1;
  else if(m == 2)//两个格子的走法总数
    return 2;
  else if(m == 3)//三个格子的走法总数
    return 4;
  else if(m == 4)//四个格子的走法总数
    return 8;
  else if(m == 5)//改步骤若不使用f(n) = 2*f(n-1)-f(n-5)可以省略
    return 15;
  if(vis[m])//用于递归返回时标记,若为true,则直接带回ans[m],避免回归时重复访问
    return ans[m];
  vis[m] = true;//递推时,每次都标记为true
  return ans[m] = function(m-1)+function(m-2)+function(m-3)+function(m-4);
  // return ans[m] = function(m-1) + function(m-1) - function(m-5);
}

 

   在第一次提交时,只拿了9分,最后一个6分的测试点报运行超时了,对于目前递归算法加上记忆化搜素已经是我能够想到的最大程度上优化时间效率的方法了。于是我就上CSDN上搜资料,意外发现可以用BufferedReader类来完成数据输入。BufferedReader类的时间效率是Scanner类的两倍。于是我将输入换成使用BufferedReader类试了一下,结果证明时间效率确实更高,在运行超时被卡时可以考虑使用。

   以下是BufferedReader类使用方法的简单示例:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

 

BufferedReader input=new BufferedReader(new InputStreamReader(System.in));

int n = 0,m = 0;
try {
  n = Integer.parseInt(input.readLine());
} catch (Exception e) {
  e.printStackTrace();
}

 

 

         注意:BufferedReader 类使用前需要导入对应的类包,即import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader。

 

PTA第三次作业

  7-1的菜单系列题目就不在此过多吐槽了,仍然是一道难以跨越的鸿沟。本次作业时限只有六天,但是题量并未因此而减少,足有七道题目。最关键的是这七道题目,没有一道是水题,有的是设计思路复杂,有的是除题面样例外的测试点很难通过,有的是卡你一个小知识点。总而言之,要按时按质的完成并不是一件简单的事情。真的是会被压的喘不过气的。但是收获还是满满的,尤其当自己做完所有题目的时候,那种如释重负,自豪的感觉还是很美妙的。

 知识储备

         sort快速排序;集合set(包括Hash和Treeset);字典序(字典序排序);构造方法;控制浮点型数据输出格式(例如小数点位数等);Integer类中parseInt()等方法的用法,了解LocalDate类中of()、isAfter()、isBefore()、until()等方法的使用规则,了解ChronoUnit类中DAYS、WEEKS、MONTHS等单位的用法。

    知识点

         7-2 有重复的数据:sort排序,判断重复出现问题。

   7-3 去掉重复的数据:sort排序,重复数据的删除问题。

   7-4 单词统计与排序:sort自定义排序字典序排序indexOf方法查找指定字符。

   7-5 面向对象编程(封装性):构造函数

   7-6 简单的计算(下文就不做解释说明了,也没有特别的坑)。 

   7-7 判断两个日期的先后,计算间隔天数、周数:了解Calender类或者Date类、ChronoUnit类等可用于计算获取日期的类的使用。

 设计与分析

   7-2与7-3:重复数据问题

   这两题是同一类型得题目,主要考察对于重复数据类型的处理问题,即查找是否有重复数据和删除重复数据。这两题的难点就在于运行超时上,需要使用时间效率较高的算法,或者说能够优化时间效率才能AC。暴力查找和删除会超时。

   7-2中我使用的是sort排序来降低程序的时间复杂度,暴力方法时间复杂度未O(n^2),而sort排序的时间复杂度为n*logn。刚着手本题的时候并没有想到使用sort排序,而是直接暴力查找了,自然最后两个“很慢”的测试点就无法通过了(虽然现在我也不清楚这两个测试点具体是什么意思,但是肯定是和时间复杂度有关的,也就是运行超时的问题)。于是我就换用了sort排序的方法,刚开始并不会使用,接触的sort排序都是C和C++中的,无奈只能上CSDN搜索学习了,一番磕磕碰碰之后还好解决了。

   以下展示sort排序的代码:

以下两行是必要的类包

import java.util.Arrays;
import java.util.Comparator;

Arrays.sort(arr);

//判断是否存在重复数据
int now_int = arr[0],judge = 0;//now_int用于记录当前数据;judge用于判断是否有重复数据(0表示没有,1表示有)
for(int i = 1;i < n;i++)
{
  if(now_int != arr[i])
    now_int = arr[i];
  else
  {
    judge = 1;
    break;
  }
}

 

注意:使用sort排序需要包含对应的类包

   7-3这道题借用集合(HashsetTreeset都可以)可以很好的解决重复数据删除。但是我用了一种投机取巧的方式,这道题目中,老师设置的难过的测试点就是运行超时,而没有在空间上来卡我们。于是我就直接在开辟了一个数组,数组大小就是输入数据的最大值,将输入的当前数据值作为下标,每输入一个数据,该数组下标对应元素的值+1。这样会出现的问题就是内存可能会开的很大,例如输入数据为“1 1000”,那么开的内存大小就是1000了。但是神奇的是竟然这样过了这题,哈哈!

   以下展示该方法代码: 

int[] cnt = new int[max+1];//记录元素出现次数,max为输入数据最大值
int judge = 0;//判断是否是第一次输入
for(int i = 0;i < n;i++)
{
  if(cnt[arr[i]] == 0)//如果只出现一次,则输出
  {
    if(judge == 0)
      System.out.print(arr[i]);
    else
      System.out.print(" "+arr[i]);
  }
  cnt[arr[i]]++;//当前下边对应元素的值+1
  judge++;
}

 

 

          7-4:单词统计与排序

           这题依然可以使用集合解决。我在本题中使用的是sort的自定义排序法。这题好拿分,但是很难通过全部测试点,因为该题中有个很阴间的测试点,就是单词前面出现标点符号(即逗号或者句号)时,单词长度并不是原本的单词长度,而是加上了标点后的长度。我就在这个点上卡了好久,一直找不到症结所在。这题只要自测输入测试样例的时候找到了这个坑,就很好说了。没找到的话,就另当别论了,也就是掉些头发的事情,这是每个程序员都必须经受的过程,所以大家不必担忧,早掉晚掉都得掉。
           以下展示sort自定义排序得用法:

以下两行是必要的类包

import java.util.Arrays;
import java.util.Comparator;

delete.function(arr);//删除标点符号,这部分一定要在排序前完成
//自定义排序,优先按长度排序,其次按字典序排序
Arrays.sort(arr, new Comparator<String>()
{
  public int compare(String a, String b)
  {
    if(b.length()>a.length())
      return 1;
    else if(b.length()<a.length())
      return -1;
    else
      return a.compareToIgnoreCase(b);
  }
});

 

注意:使用sort排序需要包含对应的类包,删除标点符号一定要在排序前完成。

 

           7-5:面向对象编程(封装性)

           经过前面的修炼过程,这题没有什么好说道的了。唯一需要知道的就是构造函数的相关知识点。

           ①、当通过new来创建一个类时,Java虚拟机会自动运行类中的构造函数。

           ②、如果在编写一个类时,没有编写构造函数,则系统会提供一个默认的无参构造函数;

           ③、如果类中提供了至少一个构造函数,但是没有提供无参构造函数,则在构造对象时如果没有提供参数就会被视为不合法;

           ④、仅当类没有提供任何构造函数时,系统才会提供一个默认的构造函数。

           ⑤、当用户显示定义了构造函数时(通常是用户构造了含参构造函数),系统不在生成无参默认构造函数;即当构造含参构造函数时,务必要自己编写无参默认构造函数。

            以下通过本题构造函数部分加以展示:

//以下展示class类中的构造函数部分

//构造函数
Student()
{

}
//设置学生的信息
Student(String sid2,String name2,int age2,String major2)
{
  sid = sid2;
  name = name2;
  age = age2;
  major = major2;
}

 

 

            若将标红段代码删除,编译器运行则会报错,运行结果如下:

 

            7-7:判断两个日期的先后,计算间隔天数、周数

            本题与PTA第二次作业中的7-3jmu-java-日期类的基本使用考察知识点类似,依然是考察与日期相关类的使用,可以选择Calender类或者Date类、ChronoUnit类等,能用于计算日期即可。需要注意的是本题中的日期输入格式并不是固定的,例如可以按照“YYYY-XX-MM”也可以按照“YYYY-X-M”

的格式输入。此处就不在过多展示,可以参考上文7-3jmu-java-日期类的基本使用一题。该题所用到的知识点在7-3jmu-java-日期类的基本使用中已有展示。

 

 菜单系列

            截至目前菜单系列一共发布了三次题目,内容和功能在不断丰富,难度也在逐渐提升。以下大致简述一下这三次题目分别包含什么功能。

            菜单计价程序-1中主要还是实现最基础的单桌点菜功能,当然,既然有点菜,那么自然也就包含菜谱,也得判断该道菜品是否存在,以及计算总价。在菜单计价程序-1中还是处于形成菜单系列雏形的状态,让大家先适应、熟悉该系列的类的设计与使用,后续题目也是在此基础上延伸的。

难度-较易

            菜单计价程序-2在菜单计价程序-1的基础上添加了删除点菜记录的功能,以及输出每条点菜记录功能。

难度-适中

            菜单计价程序-3真可谓是“前方一大波功能来袭”。其在菜单计价程序-2的基础上添加了桌的概念,和代点菜功能。同时添加了点菜时间和折扣价,需要通过点菜时间来判断当前是否在营业时间。若在,需进一步判断当前的折扣价。

难度-偏难 

 设计与分析

             首先,简单介绍一下圈复杂度的概念。圈复杂度(Cyclomatic Complexity, 简称‘CYC’)是一种用于确定程序复杂度的软件度量指标。它是对源代码中决策(decision)数量的计数。计数越高,代码越复杂。

McCabe&Associates 公司建议尽可能使 圈复杂度V(G) <= 10。NIST(国家标准技术研究所)认为在一些特定情形下,模块圈复杂度上限放宽到 15 会比较合适。

因此圈复杂度 V(G)与代码质量的关系如下:
V(G) ∈ [ 0 , 10 ]:代码质量不错,可读性强,维护成本较低;
V(G) ∈ [ 11 , 15 ]:代码质量一般,可读性适中,维护成本适中,可能存在需要拆分的代码,应当尽可能想措施重构。;
V(G)∈ [ 16 , ∞ ):代码质量极差,不具有可读性,维护成本极高,必须进行重构;

一、菜单计价程序-1

类图:

 

 

圈复杂度:

详解各个类的属性和方法          

菜品类:对应菜谱上一道菜的信息。

Dish {

  String name;//菜品名称
  int unit_price; //单价
  int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
}

 

菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu {
  Dish[] dishs ;//菜品数组,保存所有菜品信息
  Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
}

 

点菜记录类:保存订单上的一道菜品记录
Record {
  Dish d;//菜品
  int portion;//份额(1/2/3代表小/中/大份)
  int getPrice()//计价,计算本条记录的价格
}

订单类:保存用户点的所有菜的信息。

Order {
  Record[] records;//保存订单上每一道的记录
  int getTotalPrice()//计算订单的总价
  Record addARecord(String dishName,int portion)//添加一条菜品信息到订单中
}

          该题的类是老师设计好的,我们只需要理解并按照老师的设计操作即可,因此整个程序的平均圈复杂度较低,仅为3.83,代码质量、可读性和可维护性都不错。在编写本题的时候,由于这是首次接触菜单系列题目,或者说首次接触完全意义上的类的设计与使用,所以最大的困难不是过测试点,而是如何理解这些类的属性和方法的使用,以及各个类和方法间的调用关系。

          以下简单分享以下我的设计思路:①先在菜谱类中初始化菜品信息;②在主方法中循环输入,通过调用Order订单类中的addARecord方法,添加订单消息;③addARecord方法中调用Menu菜谱类中的searthDish方法,用于设置Record类种的属性d,d中包含当前点菜记录所点菜品的份额和单价。若当前所点的菜品菜品不存在,则将单价设置为0;④循环结束后,先遍历订单消息,查找各条点菜记录中菜品对应单价是否为0。若为0,则输出菜品不存在的对应提示信息“菜名+does not exist”;⑤最后,调用Order类中的getTotalPrice方法计算总价并输出。getTotalPrice方法中调用Record类中的方法getPrice,计算本条记录的价格,Record类中的方法getPrice实际上并不执行计价功能,而是通过调用Dish类中的getPrice实现计价功能。

          这题的难点就在于如何设计思路,理解老师所给出的类图。在有了方向之后,将思路通过代码编写出来并不困难,代码量也不算长,容易出错的地方可能就是非零返回的问题。这道题目应该还没有普遍性的涉及到非零返回问题,对于非零返回问题将在菜单计价程序-2中详细解释。

  以下通过主方法的代码展示我的设计与思路:

public static void main(String[] args)
{
  Scanner input = new Scanner(System.in);
  Dish dish = new Dish();//定义菜品类
  Menu menu = new Menu();//定义菜谱类
  Record record = new Record();//定义点菜记录类
  Order order = new Order();//定义订单类
  String name = new String();//存储输入的菜名
  String illegal = "end";//用于判断输入的菜名是否能识别
  int portion = 0,i=0,cnt = 0;//cnt用于计入订单信息条数,portion为输入的份额
  while(true)//循环输入
  {
    name = input.next();
    if(name.compareTo(illegal)==0)//判断是否结束输入
      break;
    dish.name = name;
    portion = input.nextInt();
    order.records[i] = new Record();
    order.records[i]=order.addARecord(dish.name,portion);//存储订单信息
    i++;
    cnt++;
  }
  for(int k=0;k<cnt;k++)//查找不能识别的菜名并完成对应的输出
  {
    if(order.records[k].d.unit_price==0)
    System.out.println(order.records[k].d.name+" does not exist");
  }
  int sum_price = 0;
  sum_price = order.getTotalPrice(cnt);//计算订单总价
  System.out.println(sum_price);
}

二、菜单计价程序-2

类图:

 

 

圈复杂度:

 

 详解各个类的属性和方法

         以下列举在菜单计价程序-1的基础上有所改变的类(橙色字体部分)。

菜谱类:对应菜谱,包含饭店提供的所有菜的信息

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)//添加一条菜品信息到订单中。
  findRecordByNum(int orderNum)//根据序号查找一条记录
}

  该题在菜单计价程序-1的基础上新增了删除点菜记录的功能,以及输出每条点菜记录功能。这也就导致了类的设计和使用需要发生大幅度的改变。         1、 首先菜谱信息不在是初始化好的,而需要根据输入数据实时更新,所以在Menu类中添加了addDish方法,用于更新菜谱信息。

       2、 由于Order类中的方法涉及按序号查找功能,所以在Record类中添加了序号属性orderNum,用于标记每条点菜记录。

       3、 该题由于需要输出每条点菜记录的价格,而输出的信息需要包括序号,菜名,份额,份数,本条记录价格,所以在Order类中的addARecord方法,添加菜品信息到订单中时,需要再传入菜名dishName、份额portion、份数num三个参数。

       4、findRecordByNum方法通过参数orderNum(即序号)索引,找到对应点菜记录。

       一点小技巧:四舍五入

class Dish//菜品类
{
  String name;//菜品名称
  int unit_price;//单价
  int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据1/2/3,代表小/中/大份)
  {
    switch(portion)//设置当前点菜价格
    {
      case 1: return unit_price;
      case 2: return (int)Math.ceil((double)unit_price*3/2);//计算出现小数时,按四舍五入的规则进行处理
      case 3: return unit_price*2;
    }
    return 0;
  }
}

 

 

 

 

 

我的Bug:

       在编写代码的过程中,最大的问题就是非零返回和空指针异常(即访问到了地址内容为空的元素)。空指针异常在最初接触该题的时候相信是大家都很懊恼的一个问题,空指针异常的报错如下:java.lang.NullPointerException: Cannot read field "******" because "******" is null。看到这个报错,第一反应就是“寄,CPU烧了”。但是在知道这是因为访问到空的元素或者说地址后,就不会再觉得CPU被干爆了。这种错误通常就是因为没有new出相应的类的对象,或者创建了类的数组,但是类的数组元素没有new出来;在使用数组执行数组下标加加时,可能没有理清楚逻辑,数组下标少加了一次,导致空指针报错。这也是我经常容易犯的错误。

       非零返回和空指针报错的报错点大致相同,这里就不在重复赘述了。需要注意的是非零返回可能存在很低级很低级的错误,就是在编译器上编写完代码,粘贴到PTA提交时忘记删除“package”,这种细节上的问题还是需要多多注意的,我就在这上面吃了大亏,直接哭死。

 

三、菜单计价程序-3

类图:

 

 

 

圈复杂度:

 

 详解各个类的属性和方法

  这道题目与菜单计价程序-2相比来说,难度大的不是一丁点,真可谓是扶摇直上啊!从功能上来看也就只加了个桌类的概念,以及代点菜的功能,但是代码量是实打实的加了上百多行啊。

  本题中新添加的类其实就一个桌类,以下是我的桌类的设计思路:

class Order//订单类
{
  int sum_price = 0;//存储订单总价
  int delete_error_cnt=0;
  Record[] records = new Record[100];//保存订单上每一道的记录
  int getTotalPrice(int cnt)//计算订单的总价
  Record addARecord(int orderNum,String dishName,int portion,int num,int n,Menu menu)//添加一条菜品信息到订单中
  void findRecordByNum(int orderNum,int cnt)//根据序号查找一条记录
}

class Table
{
  Order order = new Order();
  int tableNum;//桌号
  String time;//点餐时间
  int discount;//记录当前时间的折扣
  boolean work = false;//判断当前是否在营业状态,1表示在营业
}

 

 

 

          接下来详细介绍关键类的设计思路与使用:

class Menu类//菜谱类

方法addDish---传入输入的菜名,菜品单价
         数据类型:dishName--主方法中输入的菜名,赋值给菜谱数组中的菜品名称(dish[].name)
            unit_price--主方法中输入的菜价,赋值给菜谱数组中的菜品单价(dish[].unit_price)
        处理:将传入(或者说输入)的菜名和单价通过return返回值的形式带回给Menu类(菜谱类)中的dishs数组(菜谱)
方法searthDish---传入菜名,序号,菜谱
        参数:dishName--输入的菜名,用于和菜谱中的菜名比对(new_menu.dishs[i].name)
           name_count--表示菜谱中的菜品总数,用于控制循环次数
           new_menu--菜谱
         处理:根据菜名在菜谱中查找菜品信息,返回Dish对象

class Record//点菜记录类

数据类型:orderNum--该条点菜记录的序号
     num--该条点菜记录的菜品份数
     portion--该条点菜记录的菜品份额
     d--菜品类的对象,用于存储菜品名称和菜价
 方法getprice---传入输入的菜名,菜品单价
 数据类型:dishName为主方法中输入的菜名,赋值给菜谱数组中的菜品名称(dish[].name)
      unit_price为主方法中输入的菜价,赋值给菜谱数组中的菜品单价(dish[].unit_price)
 处理:计价,计算本条记录的价格--通过调用菜品类对象d的方法找到菜价,再乘以份额得到该条点菜记录的价格
          完成单挑点菜记录价格和订单中包含不能识别的菜名的输出

 class Order//订单类

数据类型:sum_price--用于该类的方法getTotalPrice中,记录菜品总价
     delete_error_cnt--用于该类的方法findRecordByNum中,记录错误删除的次数
     records[]--订单数组
 方法getTotalPrice---传入订单总数
 参数:参数为int--订单总数,用于控制循环次数
 处理:循环遍历,通过调用订单类records[i]的方法getPrice计算每条订单价钱,累加到sum_price中,返回订单总价
 方法addARecord---传入序号、菜名、份额、份数、菜谱中的菜品总数、菜谱
 参数:dishName--菜名,传入调用的菜谱类的方法searthDish中
    n--菜谱中的菜品总数,传入调用的菜谱类的方法searthDish中
         menu--菜谱、传入调用的菜谱类的方法searthDish中
      portion--份额
      orderNum--序号
 处理:通过调用菜谱类menu的方法searthDish的return返回设置点菜记录中菜品类对象d
    完成添加一条菜品信息到订单中
 方法findRecordByNum---传入序号,订单信息总数
 处理:计算错误删除的次数

  强烈吐槽这题的测试点,一直卡在倒数的那三个测试点,就是因为我们亲爱的老师给的数据有问题,或者说题目有问题(这么说可能有点直白了,哈哈)。周末营业时间应该是9:30到21:30,但是题目原本给的是9:30到21:00,导致那三个测试点一直过不了,给大家伙难受的呀!刚刚看了下题目,亲爱的蔡老师不知道啥时候悄咪咪滴把时间改过来了。

  至于Bug就不再说什么了,出错的地方依旧和菜单计价程序-2中所讲述的一样,在于非零返回和空指针异常。

踩坑心得

   这里简单总结以下在这三次题集中踩的各种坑的心得和经验吧,有一说一,坑是真坑啊!

  1、PTA中编写代码不会实时报错,代码量一多,运行时可能一下报出十几个错,CPU都给看没了,所以建议在eclipse或者idea等编译器上编写。

  2、在使用类中的属性或者方法时,务必记得创建对象;同时在创建类的数组,需要使用其元素时也需要对当前元素new一下,否则会报空指针或者非零返回。

  3、使用类时一定要new明白,不能重复创建,重复创建会导致当前所使用的类中的数据变为空,出现空指针或者非零返回的问题,切记切记。

  4、类与类间是平行的,不能在类中创建类。

  5、在类中不能调用属性和方法,只有在方法中才能调用。

  6、对于带返回值的感受务必要记得return,否则会非零返回,在编译器上编写代码时如果没有带回返回值·会报错,但是PTA编译却没问题,这时候编译器实时报错的好处又在此体现出来了。

  7、在编写代码时遇到的很多问题可能都和圈复杂度较高有关,例如运行超时等。所以编写代码时,可以借用SourceMonitor来查看圈复杂度,尽量控制在8以内(这样代码可读性较高,修改也方便)。

  8、还有就是一些细节语法上的错误,要尽量减少这类失误,虽然这个没什么好说的,但是很多问题的出现都是因为一个个细节没有处理好而累积导致的后果。

 

主要困难以及改进建议

   主要困难:我个人认为对于这三次的PTA大作业来说,主要的困难不是题目难不难,而是没有花费足够的时间去学习相应的Java的知识。每次大作业发布的时候都需要临时去学习相应的内容,例如对于第二次大作业中7-3 jmu-java-日期类的基本使用,因为没有学过日期类而不知道如何着手,第三次大作业中7-2和7-3重复数据的问题,我知道sort排序、set集合在C++中怎么使用,但是没有学习过在Java中的用法,最大的问题就是菜单系列的类的设计与使用,让我做起来很吃力。所以说,没有平时日积月累的训练学习,到做大作业的时候问题集中起来的时候,就会感觉很吃力,也会影响自己的心情,甚至进而形成恶性循环。

  改进建议:当然,这主要还是归咎于个人学习的自觉性,但是外界环境也具备一定的影响。在作业布置中,我认为可以将作业细化一些,发布作业的频率适当提高,作业量适当减少,作业难度也适当降低,当然,时限自然而然也是要减少的。人的潜力和上限是无穷的,在巨大的压力下,才能够爆发出力量。常言道,不逼一逼,怎知自己有多优秀。

 

总结

 一、对Bug的小感悟      

  bug通常来自三种原因:

  1、对题面理解的疏忽

  2、对自己代码架构细节出现问题(使用错误方法)

  3、粗心导致

  4、知识的缺乏

二、学到的东西

  1、对正则表达式的使用

  2、熟悉理解了类的设计与使用;

  3、对面向对象编程(封装性)有了初步的认识;

  4、能够自如的使用eclispe编译器;

  5、对于各种报错能够理解、从容的去修改;

  6、能够熟练对代码进行调试,查找调试过程中代码的各类数据的变化等;

  7、当然,我认为最大的收获,还是对与Java这门语言有了一定的理解,能够逐渐窥见面向对象的神秘之处;

  8、对于Java的学习还是不能够松懈,不能只跟着老师的进度走,需要将进度提高一些,否则面对大作业就会显得吃力了。

posted @ 2023-04-01 22:59  欧阳东  阅读(286)  评论(0)    收藏  举报