OOP第二阶段(五到十周)作业总结
一、前言
在第二阶段作业中,三次训练集得分依次是70、100、67
训练集04和训练集05题目难度较为简单,训练集04主要的知识点涉及Java对数据的处理,判断是否有重复数据、对重复数据的去除、对数据的排序和对数据的计算,对正则表达式的运用,以及面向对象相关的知识,比如训练集04 7-5 考察对类的封装设计、训练集05 7-5 7-6 考察类与类之间聚合关系的设计
训练集06题目难度比较困难,题目刚发布,老师就在群里温馨提示:本次题目集只有一道题目,难度较大,预计代码行数:1000+,需用时:20h+,建议尽早开始,尽早完成。我也是在题目发布下来的第一时间打开,近3000字的题目描述,写的过程也十分坎坷(后面再细说),最后只得了67分。题目涉及的算法不难,主要还是考察了面向对象程序设计,设计类、类与类的关系、类的方法以及对类的引用。
二、设计与分析
OOP训练集05 7-5 日期问题面向对象设计(聚合一)
类图如下:
设计分析与心得:先设计好DateUtil类、Day类、Month类、Year类之间的关系,根据题目要求:Day类聚合DateUtil类、Month类聚合Day类、Year类聚合Month类,再编写各个类中相应的属性、类构造方法及类方法,比如在Month类中int类型的value属性用于处理月份信息,Year类型的year属性用于组合Year类处理数据,restMin方法将月份设置为1,restMax方法将月份设置为12,validate方法检验数据的合法性,monthIncrement方法将月份值加1,monthReduction方法将月份值减1。
由于此题是之前OOP训练集03 7-3定义日期类的迭代,于是乎编写的过程顺利,使用聚合的设计也让在求日期前n天和后n天容易,一遍通过了所有测试点。
与OOP训练集03 7-3定义日期类相比较,都将类进行了封装处理,不同的是聚合一将Year、Month、Day的属性和方法分别做成了单独的类。提高了代码的可读性,更容易维护;使得系统更灵活、更容易扩展,而且成本较低;接近于日常生活和自然的思考方式,势必提高软件开发的效率和质量。
源码如下
import java.util.*;
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.validate()) {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
m = input.nextInt();
if (m < 0) {
System.out.println("Wrong Format");
input.close();
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(year, month, day);
if (!date.validate()) {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
n = input.nextInt();
if (n < 0) {
System.out.println("Wrong Format");
input.close();
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(year, month, day);
DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay);
if (fromDate.validate() && toDate.validate()) {
System.out.println(fromDate.getDaysofDates(toDate));
} else {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
} else {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
input.close();
}
}
class Year {
private int value;
public Year() { // 默认构造方法
}
public Year(int yearValue) { // 带参构造方法
this.value = yearValue;
}
public int getValue() { // value gettter
return value;
}
public void setValue(int yearValue) { // value setter
this.value = yearValue;
}
public boolean isLeapYear() { // 判断是否为闰年
return (this.value % 4 == 0 && this.value % 100 != 0)
|| this.value % 400 == 0;
}
public boolean validate() { // 效验数据合法性
return this.value <= 2050 && this.value >= 1900;
}
public void yearIncrement() { // year + 1
this.value++;
}
public void yearReduction() { // year - 1
this.value--;
}
}
class Month {
private int value;
private Year year;
public Month() { // 默认构造方法
}
public Month(int yearValue, int monthValue) { // 带参构造方法
this.year = new Year(yearValue);
this.value = monthValue;
}
public int getValue() { // value getter
return value;
}
public void setValue(int monthValue) { // value setter
this.value = monthValue;
}
public Year getYear() { // year getter
return year;
}
public void setYear(Year year) { // year setter
this.year = year;
}
public void resetMin() { // 月份复位(1)
value = 1;
}
public void resetMax() { // 月份设置为12
value = 12;
}
public boolean validate() { // 效验数据合法性
return value >= 1 && value <= 12;
}
public void monthIncrement() { // month + 1
this.value++;
if (this.value > 12) { // 下一年
this.resetMin();
this.year.yearIncrement();
}
}
public void monthReduction() { // month - 1
this.value--;
if (this.value < 1) { // 前一年
this.resetMax();
this.year.yearReduction();
}
}
}
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 };
public Day() { // 默认构造方法
}
public Day(int yearValue, int monthValue, int dayValue) { // 带参构造方法
this.month = new Month(yearValue, monthValue);
this.value = dayValue;
}
public int getValue() { // value getter
return value;
}
public void setValue(int dayValue) { // value setter
this.value = dayValue;
}
public Month getMonth() { // month getter
return month;
}
public void setMonth(Month month) { // month setter
this.month = month;
}
public void resetMin() { // 日期复位(1)
value = 1;
}
public int getMon_Maxnum(int n) {
if (this.getMonth().getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
return mon_maxnum[n];
}
public void resetMax() { // 日期改为月最大值
if (this.getMonth().getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
value = mon_maxnum[month.getValue()];
}
public boolean validate() { // 效验数据合法性
if (this.getMonth().getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
return value >= 1 && value <= mon_maxnum[month.getValue()];
}
public void dayIncrement() { // day + 1
this.value++;
if (this.getMonth().getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
if (this.value > this.getMon_Maxnum(this.getMonth().getValue())) {
this.getMonth().monthIncrement(); // 下一月
this.resetMin();
}
}
public void dayReduction() { // day - 1
this.value--;
if (this.getMonth().getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
if (this.value < 1) {
this.month.monthReduction(); // 前一月
this.resetMax();
}
}
}
class DateUtil {
private Day day;
public DateUtil() { // 默认构造方法
}
public DateUtil(int yearValue, int monthValue, int dayValue) { // 带参构造方法
this.day = new Day(yearValue, monthValue, dayValue);
}
public Day getDay() { // day getter
return day;
}
public void setDay(Day day) { // day setter
this.day = day;
}
public boolean validate() { // 效验数据合法性
// return day.validate();
return this.getDay().getMonth().getYear().validate() && this.getDay().getMonth().validate()
&& this.getDay().validate();
}
public boolean compareDates(DateUtil date) { // 比较两个日期的大小
boolean flag = false;
if (this.getDay().getMonth().getYear().getValue() > date.getDay().getMonth().getYear().getValue()) {
flag = true; // 先比较年
} else if (this.getDay().getMonth().getYear().getValue() == date.getDay().getMonth().getYear().getValue()
&& this.getDay().getMonth().getValue() > date.getDay().getMonth().getValue()) {
flag = true; // 年相同比较月
} else if (this.getDay().getMonth().getYear().getValue() == date.getDay().getMonth().getYear().getValue()
&& this.getDay().getMonth().getValue() == date.getDay().getMonth().getValue()
&& this.getDay().getValue() > date.getDay().getValue()) {
flag = true; // 年、月相同比较天
}
return flag;
}
public boolean equalTwoDates(DateUtil date) { // 判定两个日期是否相等
return this.getDay().getMonth().getYear().getValue() == date.getDay().getMonth().getYear().getValue()
&& this.getDay().getMonth().getValue() == date.getDay().getMonth().getValue()
&& this.getDay().getValue() == date.getDay().getValue();
}
public String showDate() { // 日期值格式化
return this.getDay().getMonth().getYear().getValue() + "-" + this.getDay().getMonth().getValue() + "-"
+ this.getDay().getValue();
}
public DateUtil getNextNDays(int n) { // 求下 n 天
while (n-- > 0) {
this.getDay().dayIncrement(); // 下一天
}
return this;
}
public DateUtil getPreviousNDays(int n) { // 求前 n 天
while (n-- > 0) {
this.getDay().dayReduction(); // 前一天
}
return this;
}
public int getDaysofDates(DateUtil date) { // 求两个日期之间的天数
int sum = 0;
while (this.equalTwoDates(date) == false) {
sum++;
if (this.compareDates(date)) {
this.getDay().dayReduction();
} else {
date.getDay().dayReduction();
}
}
return sum;
}
}
OOP训练集05 7-5 日期问题面向对象设计(聚合二)
类图如下:
设计分析与心得:本题与上一题相似,只是改变了聚合关系:Year类聚合DateUtil类、Month类聚合DateUtil类、Day类聚合DateUtil类,类中的方法也相应改变了一些,但是算法不变。
只是没仔细看题,没发现两题的输出格式和日期限制不同,错了一次(说明要仔细看题哈),改完后就对了
源码如下:
import java.util.*;
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.checkInputValidate()) {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
m = input.nextInt();
if (m < 0) {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
System.out.print(date.showDate() + " 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.checkInputValidate()) {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
n = input.nextInt();
if (n < 0) {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
System.out.print(date.showDate() + " 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.checkInputValidate() && toDate.checkInputValidate()) {
System.out.println("The days between " + fromDate.showDate() +
" and " + toDate.showDate() + " are:"
+ fromDate.getDaysofDates(toDate));
} else {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
} else {
System.out.println("Wrong Format");
input.close();
System.exit(0);
}
input.close();
}
}
class Year {
private int value;
public Year() { // 默认构造方法
}
public Year(int yearValue) { // 带参构造方法
this.value = yearValue;
}
public int getValue() { // value gettter
return value;
}
public void setValue(int yearValue) { // value setter
this.value = yearValue;
}
public boolean isLeapYear() { // 判断是否为闰年
return (this.value % 4 == 0 && this.value % 100 != 0)
|| this.value % 400 == 0;
}
public boolean validate() { // 效验数据合法性
return this.value <= 2020 && this.value >= 1820;
}
public void yearIncrement() { // year + 1
this.value++;
}
public void yearReduction() { // year - 1
this.value--;
}
}
class Month {
private int value;
public Month() { // 默认构造方法
}
public Month(int monthValue) { // 带参构造方法
this.value = monthValue;
}
public int getValue() { // value getter
return value;
}
public void setValue(int monthValue) { // value setter
this.value = monthValue;
}
public void resetMin() { // 月份复位(1)
value = 1;
}
public void resetMax() { // 月份设置为12
value = 12;
}
public boolean validate() { // 效验数据合法性
return value >= 1 && value <= 12;
}
public void monthIncrement() { // month + 1
this.value++;
}
public void monthReduction() { // month - 1
this.value--;
}
}
class Day {
private int value;
public Day() { // 默认构造方法
}
public Day(int dayValue) { // 带参构造方法
this.value = dayValue;
}
public int getValue() { // value getter
return value;
}
public void setValue(int dayValue) { // value setter
this.value = dayValue;
}
public void dayIncrement() { // day + 1
this.value++;
}
public void dayReduction() { // day - 1
this.value--;
}
}
class DateUtil {
private Year year;
private Month month;
private Day day;
private int[] mon_maxnum = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
public DateUtil() { // 默认构造方法
}
public DateUtil(int yearValue, int monthValue, int dayValue) { // 带参构造方法
this.year = new Year(yearValue);
this.month = new Month(monthValue);
this.day = new Day(dayValue);
}
public Year getYear() { // year getter
return year;
}
public void setYear(Year year) { // year setter
this.year = year;
}
public Month getMonth() { // month getter
return month;
}
public void setMonth(Month month) { // month setter
this.month = month;
}
public Day getDay() { // day getter
return day;
}
public void setDay(Day day) { // day setter
this.day = day;
}
public void setDayMin() { // 设置日期最小值(1)
this.getDay().setValue(1);
}
public void setDayMax() { // 设置日期最大值(当月最大天数)
if (this.getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
this.getDay().setValue(this.mon_maxnum[this.getMonth().getValue()]);
}
public boolean checkInputValidate() { // 效验数据合法性
if (this.getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
return this.getYear().validate() &&
this.getMonth().validate() &&
this.getMonth().getValue() >= 1 &&
this.getMonth().getValue() <= this.mon_maxnum[this.getMonth().getValue()];
}
public DateUtil getNextNDays(int n) { // 求下 n 天
while (n-- > 0) {
if (this.getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
this.getDay().dayIncrement(); // 下一天
if (this.getDay().getValue() > this.mon_maxnum[this.getMonth().getValue()]) {
this.getMonth().monthIncrement(); // 下一月
if (this.getMonth().getValue() > 12) {
this.getYear().yearIncrement(); // 下一年
this.getMonth().resetMin();
}
this.setDayMin();
}
}
return this;
}
public DateUtil getPreviousNDays(int n) { // 求前 n 天
while (n-- > 0) {
if (this.getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
this.getDay().dayReduction(); // 前一天
if (this.getDay().getValue() < 1) {
this.getMonth().monthReduction(); // 前一月
if (this.getMonth().getValue() < 1) {
this.getYear().yearReduction(); // 前一年
this.getMonth().resetMax();
}
this.setDayMax();
}
}
return this;
}
public boolean compareDates(DateUtil date) { // 判定两个日期的先后
boolean flag = false;
if (this.year.getValue() > date.year.getValue()) {
flag = true; // 先比较年
} else if (this.getYear().getValue() == date.getYear().getValue()
&& this.getMonth().getValue() > date.getMonth().getValue()) {
flag = true; // 年相同比较月
} else if (this.getYear().getValue() == date.getYear().getValue()
&& this.getMonth().getValue() == date.getMonth().getValue()
&& this.getDay().getValue() > date.getDay().getValue()) {
flag = true; // 年、月相同比较天
}
return flag;
}
public boolean equalTwoDates(DateUtil date) { // 判定两个日期是否相等
return this.getYear().getValue() == date.getYear().getValue()
&& this.getMonth().getValue() == date.getMonth().getValue()
&& this.getDay().getValue() == date.getDay().getValue();
}
public String showDate() { // 日期值格式化
return this.getYear().getValue() + "-"
+ this.getMonth().getValue() + "-"
+ this.getDay().getValue();
}
public int getDaysofDates(DateUtil date) { // 求两个日期之间的天数
int sum = 0;
while (this.equalTwoDates(date) == false) { // 直到两个日期相同时
sum++;
if (this.compareDates(date)) {
if (this.getYear().isLeapYear()) {
this.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
this.mon_maxnum[2] = 28; // 平年2月有28天
}
this.getDay().dayReduction(); // 前一天
if (this.getDay().getValue() < 1) {
this.getMonth().monthReduction(); // 前一月
if (this.getMonth().getValue() < 1) {
this.getYear().yearReduction(); // 前一年
this.getMonth().resetMax();
}
this.setDayMax();
}
} else {
if (date.getYear().isLeapYear()) {
date.mon_maxnum[2] = 29; // 闰年2月有29天
} else {
date.mon_maxnum[2] = 28; // 平年2月有28天
}
date.getDay().dayReduction(); // 前一天
if (date.getDay().getValue() < 1) {
date.getMonth().monthReduction(); // 前一月
if (date.getMonth().getValue() < 1) {
date.getYear().yearReduction(); // 前一年
date.getMonth().resetMax();
}
date.setDayMax();
}
}
}
return sum;
}
}
聚合一与聚合二的比较:
聚合一与聚合二大致相同,使用了类的聚合关系,区别就在于聚合一Day类聚合DateUtil类、Month类聚合Day类、Year类聚合Month类,而聚合二Year类聚合DateUtil类、Month类聚合DateUtil类、Day类聚合DateUtil类,聚合二的耦合性更低。
聚合一:
聚合二:
可以看出聚合二的复杂程度比聚合一更低。
通过观察两个类图可以看出:聚合一各类之间的关联程度更强,层层聚合,如果一个类存在问题,将导致整个程序出现问题,耦合性较高,而聚合二各类之间的关联程度更低,Year类、Month类、Day类聚合DateUtil类,即使一个类出现了问题,其余类的功能也能实现,耦合性较低。迪米特法则告诉我们应该尽可能地减少对象之间的依赖关系,以便更轻松地维护代码,所以在以后的面向对象设计中,聚合二的设计更好。
OOP训练集04 7-1 菜单计价程序-3 & OOP训练集06 7-1 菜单计价程序-4
类图如下:
设计分析与心得:
“20+h 1000+行代码”,群消息温馨提示让我感到事情不简单,打开PTA,只有一题,怎么只有一题?20+h?1000+行代码?打开题目,3000字的描述,17个异常情况。虽然描述多,但也复杂。
先看题目描述吧,1h左右,大概看懂了题,再将题目有的四个类,Dish类、Meun类、Record类、Order类,的各类属性和方法初步编写完成。第一天就到这了。
之后就开始Main类的编写,第一到困难就将这6种情况(普通菜、特色菜、桌号标识、点菜、删除点菜、代点菜)分开,好在练习过正则表达式,就很容易地将这6种情况分开了。
然后逐一编写这6种情况,Main类越来越多,而且桌号标识需要储存和判断,加个Table类,用于储存桌号标识信息并判断信息是否合法,加个Table类还不够,再加了个Tables类,保存所有桌号标识信息并能查找桌号标识信息、判断数据是否合法、添加桌号标识信息,类似Dish类与Meun类的关系。
也完善了类中的方法,异常情况比较多,对数据进行了很多判断处理,比如
菜名要存在,否则 dishName does not exist;
点菜的订单序号必须从小到大,否则 record serial number sequence error;
普通菜份额是1、2、3,特价菜份额是1、2,否则 portion out of range;
份数不能超过15,否则 num out of range。
菜名不存在、份额超出范围、份数超出范围,按记录中从左到右的次序优先级由高到低,输出时只提示优先级最高的那个错误。
写到这,只过了4天,感觉好像快写完了,而且还有3天,很开心。NO!第一次提交就是0分,而且很多是格式错误,修改好格式后,40+分。
不久,又发现了一个很大的问题,代点菜是别的桌点菜,但是订单信息需要打印在被点菜的那桌上,那这样的话,就不能每次输入,程序就打印一个信息。我的解决方法就是:储存语句。
在Order类中添加一个String型的数组,用于储存订单信息,再最后逐一将各个订单信息按顺序输出。
修修改改,改改修修,最终只得了67分。好消息就是嘛,及格了,嗯。
后来才知道,代点菜的那桌的订单信息,要加一句“订单序号 table 代点菜桌号 pay for table 被代点菜桌号 价格”,题目要求里没说,应该在菜单计价系列的别的题中有提及。
源码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Menu menu = new Menu();
Tables tables = new Tables();
int tableCount = 0;
boolean isTableError = false;
while (true) {
String str = sc.nextLine();
if (str.equals("end")) {
break;
}
String[] splitStr = str.split(" ");
if (splitStr.length == 2 && splitStr[1].matches("[0-9]+")) { // 菜普中普通菜
if (tableCount != 0) {
tables.findTableByInedx(tableCount - 1).order.addSentence("invalid dish");
} else {
int unit_price = Integer.parseInt(splitStr[1]);
menu.addDish(splitStr[0], unit_price, false);
}
} else if (splitStr.length == 3 && splitStr[1].matches("[0-9]+")
&& splitStr[2].equals("T")) { // 菜谱中特色菜
if (tableCount != 0) {
tables.findTableByInedx(tableCount - 1).order.addSentence("invalid dish");
} else {
int unit_price = Integer.parseInt(splitStr[1]);
menu.addDish(splitStr[0], unit_price, true);
}
} else if (splitStr.length == 4 && splitStr[0].equals("table")) { // 桌子
if (splitStr[1].matches("[0-9]+")
&& splitStr[2].matches("\\d{4}/\\d{1,2}/\\d{1,2}")
&& splitStr[3].matches("\\d{1,2}/\\d{1,2}/\\d{1,2}")) {
int tableNum = Integer.parseInt(splitStr[1]);
String[] dates = splitStr[2].split("/");
String[] times = splitStr[3].split("/");
int year = Integer.parseInt(dates[0]);
int month = Integer.parseInt(dates[1]);
int day = Integer.parseInt(dates[2]);
int hour = Integer.parseInt(times[0]);
int minute = Integer.parseInt(times[1]);
int second = Integer.parseInt(times[2]);
int dayOfWeek = getDayOfWeek(splitStr[2]);
Table newTable = tables.addTable(tableNum, year, month, day, hour, minute, second, dayOfWeek);
if (newTable != null) {
isTableError = false;
tableCount++;
} else {
isTableError = true;
}
} else {
isTableError = true;
if (tableCount == 0) {
System.out.println("wrong format");
} else {
if (tableCount != 0) {
tables.findTableByInedx(tableCount - 1).order.addSentence("wrong format");
} else {
System.out.println("wrong format");
}
}
}
} else if (splitStr.length == 4 && splitStr[0].matches("[0-9]+")
&& splitStr[2].matches("[0-9]+")
&& splitStr[3].matches("[0-9]+")) { // 点菜
if (isTableError == false && tableCount > 0) {
int orderNum = Integer.parseInt(splitStr[0]);
int portion = Integer.parseInt(splitStr[2]);
int num = Integer.parseInt(splitStr[3]);
tables.findTableByInedx(tableCount - 1).order.addARecord(orderNum, splitStr[1], portion, num, menu,
true);
}
} else if (splitStr.length == 2
&& splitStr[1].equals("delete")
&& splitStr[0].matches("[0-9]+")) { // 删除
if (isTableError == false && tableCount > 0) {
int orderNum = Integer.parseInt(splitStr[0]);
tables.findTableByInedx(tableCount - 1).order.delARecordByOrderNum(orderNum);
}
} else if (splitStr.length == 5 && splitStr[0].matches("[0-9]+")
&& splitStr[1].matches("[0-9]+")
&& splitStr[3].matches("[0-9]+")
&& splitStr[4].matches("[0-9]+")) { // 代点菜
if (isTableError == false && tableCount > 0) {
int tableNum = Integer.parseInt(splitStr[0]);
int orderNum = Integer.parseInt(splitStr[1]);
int portion = Integer.parseInt(splitStr[3]);
int num = Integer.parseInt(splitStr[4]);
if (tables.findTableByNum(tableNum) != null) {
tables.findTableByInedx(tableCount - 1).order.addARecord(orderNum, splitStr[2], portion, num,
menu, false);
String addSentenceString = orderNum + " table "
+ tables.findTableByInedx(tableCount - 1).tableNum + " pay for table " + tableNum + " "
+ tables.findTableByInedx(tableCount - 1).order.findRecordByNum(orderNum).getPrice();
tables.findTableByNum(tableCount - 1).order.addSentence(addSentenceString);
} else {
if (tableCount != 0) {
tables.findTableByInedx(tableCount - 1).order
.addSentence("Table number : " + tableNum + " does not exist");
} else {
System.out.println("Table number : " + tableNum + " does not exist");
}
}
}
} else {
if (isTableError == false && tableCount == 0) {
System.out.println("wrong format");
}
if (tableCount > 0) {
tables.findTableByInedx(tableCount - 1).order.addSentence("wrong format");
}
}
}
tables.showTables();
sc.close();
}
public static int getDayOfWeek(String date) { // 找星期几
String[] arr = { "0", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
Date dateUtil = new Date();
SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd");
try {
dateUtil = f.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
int index = 0;
String temp = dateUtil.toString();
String[] temp1 = temp.split(" ");
for (int i = 0; i < arr.length; i++) {
if (temp1[0].equals(arr[i])) {
index = i;
break;
}
}
return index;
}
}
class Dish { // 菜品类:对应菜谱上一道菜的信息。
String name;// 菜品名称
int unit_price; // 单价
boolean isSpecialty;
public Dish() {
}
public Dish(String name, int unit_price, boolean isSpecialty) {
this.name = name;
this.unit_price = unit_price;
this.isSpecialty = isSpecialty;
}
int getPrice(int portion) {
int price = 0;
switch (portion) {
case 1:
price = unit_price;
break;
case 2:
price = (int) Math.round((float) unit_price * 1.5);
break;
case 3:
price = unit_price * 2;
break;
}
return price;
}
}
class Menu { // 菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Dish[] dishs = new Dish[100];// 菜品数组,保存所有菜品信息
int count = 0;
public Menu() {
}
Dish searthDish(String dishName) { // 根据菜名在菜谱中查找菜品信息,返回Dish对象。
for (int i = 0; i < count; i++) {
if (dishName.equals(dishs[i].name)) { // 找得到
return dishs[i];
}
}
return null;
}
Dish addDish(String dishName, int unit_price, boolean isSpecialty) {// 添加一道菜品信息
if (unit_price > 0 && unit_price < 300) {
for (int i = 0; i < count; i++) {
if (dishName.equals(dishs[i].name)) { // 相同菜品
dishs[i].unit_price = unit_price;
dishs[i].isSpecialty = isSpecialty;
return dishs[i];
}
}
Dish newDish = new Dish(dishName, unit_price, isSpecialty);
dishs[count] = newDish;
count++;
return newDish;
}
System.out.println(dishName + " price out of range " + unit_price);
return null;
}
}
class Record {// 点菜记录类:保存订单上的一道菜品记录
int orderNum;// 序号
Dish dish;// 菜品
int portion;// 份额(1/2/3代表小/中/大份)
int num;
public Record() {
}
public Record(int orderNum, Dish dish, int portion, int num) {
this.orderNum = orderNum;
this.dish = dish;
this.portion = portion;
this.num = num;
}
int getPrice() {// 计价,计算本条记录的价格
return dish.getPrice(portion) * num;
}
}
class Order { // 订单类:保存用户点的所有菜的信息。
Record[] records = new Record[100]; // 保存订单上每一道的记录
int count = 0;
int sum = 0;
int[] delNums = new int[60]; // 保存删除序号
int delCount = 0; // 删除个数
String[] sentences = new String[60]; // 保存语句
int senCount = 0;
public Order() {
}
public int getTotalPrice() { // 计算订单的总价
int sum = 0;
for (int i = 0; i < count; i++) {
sum += records[i].getPrice();
}
return sum;
}
public int getSpecialtyPrice() { // 特色菜总价
int sum = 0;
for (int i = 0; i < count; i++) {
if (records[i].dish.isSpecialty) {
sum += records[i].getPrice();
}
}
return sum;
}
Record addARecord(int orderNum, String dishName, int portion, int num, Menu menu, boolean isAdd) {// 添加一条菜品信息到订单中。
if (count > 0) {
if (orderNum <= records[count - 1].orderNum) { // 不按顺序
addSentence("record serial number sequence error");
return null;
// System.out.println("record serial number sequence error");
}
}
Dish dish = menu.searthDish(dishName);
if (dish == null) { // 菜名不存在
addSentence(dishName + " does not exist");
// System.out.println(dishName + " does not exist");
return null;
}
if (dish.isSpecialty == true && (portion == 1 || portion == 2)) { // 特色菜份额1、2
} else if (dish.isSpecialty == false && portion >= 1 && portion <= 3) { // 普通菜份额1、2、3
} else { // 份额超出范围
addSentence(orderNum + " portion out of range " + portion);
// System.out.println(orderNum + " portion out of range " + portion);
return null;
}
if (num <= 15) { // 份数不超过15
for (int i = 0; i < count; i++) { // 菜名和份额一样,份数相加
if (dishName.equals(records[i].dish.name) && portion == records[i].portion) { // 同一桌菜名、份额相同的点菜记录要合并成一条进行计算
records[i].num += num;
return records[i];
}
}
Record newRecord = new Record(orderNum, dish, portion, num);
records[count] = newRecord;
count++;
if (isAdd) {
addSentence(orderNum + " " + dishName + " " + newRecord.getPrice());
}
// System.out.println(orderNum + " " + dishName + " " + newRecord.getPrice());
return newRecord;
} else { // 份数超出范围
addSentence(orderNum + " num out of range " + num);
// System.out.println(orderNum + " num out of range " + sum);
return null;
}
}
void delARecordByOrderNum(int orderNum) {// 根据序号删除一条记录
for (int i = 0; i < delCount; i++) {
if (orderNum == delNums[i]) {
addSentence("deduplication " + orderNum);
// System.out.println("deduplication " + orderNum); // 删除序号重复
return;
}
}
delNums[delCount] = orderNum;
delCount++;
for (int i = 0; i < count; i++) {
if (orderNum == records[i].orderNum) { // 有删除序号
for (int j = i; j < count; j++) {
records[j] = records[j + 1];
}
count--;
return;
}
}
System.out.println("delete error");
}
Record findRecordByNum(int orderNum) {// 根据序号查找一条记录
for (int i = 0; i < count; i++) {
if (orderNum == records[i].orderNum) {
return records[i];
}
}
return null;
}
public void addSentence(String str) {
sentences[senCount] = str;
senCount++;
}
public void showSentence() {
for (int i = 0; i < senCount; i++) {
System.out.println(sentences[i]);
}
}
}
class Table { // 桌类:保存一桌的信息
int tableNum; // 桌号
int year;
int month;
int day;
int hour;
int minute;
int second;
Order order;
int dayOfWeek;
String[] dayOfWeekStrings = { "0", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
public Table() { // 无参构造方法
}
public Table(int tableNum, int year, int month, int day, int hour, int minute, int second, int dayOfWeek,
Order order) { // 带参构造方法
this.tableNum = tableNum;
this.year = year;
this.month = month;
this.day = day;
this.hour = hour;
this.minute = minute;
this.second = second;
this.dayOfWeek = dayOfWeek;
this.order = order;
}
public int getOriginalPrice() {
return order.getTotalPrice();
}
public int getDiscountPrice() {
if (dayOfWeek >= 6 && dayOfWeek <= 7) {
return order.getTotalPrice();
} else if (dayOfWeek >= 1 && dayOfWeek <= 5) {
double discount = 0;
if (hour >= 10 && hour <= 14) {
discount = 0.6;
} else if (hour >= 17 && hour <= 20) {
discount = 0.8;
}
return (int) Math.round((double) (order.getTotalPrice() - order.getSpecialtyPrice()) * discount
+ (double) order.getSpecialtyPrice() * 0.7);
}
return 0;
}
}
class Tables { // 保存每桌的信息
Table[] table = new Table[60];
int count = 0;
int tableNum; // 桌号
int year;
int month;
int day;
int hour;
int minute;
int second;
int dayOfWeek;
public Tables() {
}
public Table addTable(int tableNum, int year, int month, int day,
int hour, int minute, int second, int dayOfWeek) {
if (tableNum >= 1 && tableNum <= 55) {
Table preTable = findTableByNum(tableNum);
if (preTable == null) { // 新桌号
if (checkTimeValidity(tableNum, year, month, day, hour, minute, second)) {
if (isDuringTime(tableNum, dayOfWeek, hour, minute, second)) {
Table newTable = new Table(tableNum, year, month, day, hour, minute, second, dayOfWeek,
new Order());
table[count] = newTable;
table[count].order.addSentence("table " + tableNum + ": ");
count++;
return newTable;
}
}
} else { // 相同桌号
if (isSameTable(dayOfWeek, preTable.year, year, preTable.month, month, preTable.day, day,
preTable.hour, hour, preTable.minute, minute, preTable.second, second)) {
// 同一时间
return preTable;
} else { // 不同时间
if (checkTimeValidity(tableNum, year, month, day, hour, minute, second)) {
if (isDuringTime(tableNum, dayOfWeek, hour, minute, second)) {
Table newTable = new Table(tableNum, year, month, day, hour, minute, second, dayOfWeek,
new Order());
table[count] = newTable;
count++;
return newTable;
}
}
}
}
} else { // 桌号超出范围
if (count != 0) {
table[count].order.addSentence(tableNum + " table num out of range");
} else {
System.out.println(tableNum + " table num out of range");
}
}
return null;
}
public Table findTableByNum(int tableNum) { // 序号找桌子
for (int i = 0; i < count; i++) {
if (tableNum == table[i].tableNum) {
return table[i];
}
}
return null;
}
public Table findTableByInedx(int index) {
if (index >= 0 && index <= count) {
return table[index];
}
return null;
}
public boolean checkTimeValidity(int tableNum, int year, int month, int day, int hour, int minute, int second) {
int[] mon_maxnum = new int[] { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month >= 1 && month <= 12 && day >= 1 && day <= mon_maxnum[month]
&& hour >= 0 && hour < 24 && minute >= 0
&& minute < 60 && second >= 0 && second < 60) {
if (year >= 2022 && year <= 2023) {
return true;
} else {
if (count != 0) {
table[count].order.addSentence("not a valid time period");
} else {
System.out.println("not a valid time period");
}
}
} else {
if (count != 0) {
table[count - 1].order.addSentence(tableNum + " date error");
} else {
System.out.println(tableNum + " date error");
}
}
return false;
}
public boolean isDuringTime(int tableNum, int dayOfWeek, int hour, int minute, int second) {
if (dayOfWeek >= 1 && dayOfWeek <= 5) {
if (hour == 10) {
if (minute >= 30 && minute < 60 && second >= 0 && second < 60) {
return true;
}
} else if (hour == 20 || hour == 14) {
if (minute == 30 && second == 0) { // 20 : 30; 14 : 30
return true;
} else if (minute >= 0 && minute < 30 && second >= 0 && second < 60) {
return true;
} else {
if (count != 0) {
table[count].order.addSentence("table " + tableNum + " out of opening hours");
} else {
System.out.println("table " + tableNum + " out of opening hours");
}
return false;
}
} else if ((hour >= 17 && hour <= 19) || (hour >= 11 && hour <= 13)) {
if (minute >= 0 && minute < 60 && second >= 0 && second < 60) {
return true;
} else {
if (count != 0) {
table[count].order.addSentence("table " + tableNum + " out of opening hours");
} else {
System.out.println("table " + tableNum + " out of opening hours");
}
return false;
}
}
} else if (dayOfWeek >= 6 && dayOfWeek <= 7) {
if (hour == 9) {
if (minute >= 30 && minute < 60 && second >= 0 && second < 60) {
return true;
}
} else if (hour == 21) {
if (minute == 30 && second == 0) { // 21 : 30
return true;
} else if (minute >= 0 && minute < 30 && second >= 0 && second < 60) {
return true;
} else {
if (count != 0) {
table[count].order.addSentence("table " + tableNum + " out of opening hours");
} else {
System.out.println("table " + tableNum + " out of opening hours");
}
return false;
}
} else if (hour >= 10 && hour <= 20) {
if (minute >= 0 && minute < 60 && second >= 0 && second < 60) {
return true;
} else {
if (count != 0) {
table[count].order.addSentence("table " + tableNum + " out of opening hours");
} else {
System.out.println("table " + tableNum + " out of opening hours");
}
return false;
}
}
}
return false;
}
public boolean isSameTable(int dayOfWeek, int year1, int year2, int month1, int month2, int day1, int day2,
int hour1, int hour2, int minute1, int minute2, int second1, int sceond2) { // 是否为同一桌
if (year1 == year2 && month1 == month2 && day1 == day2) { // 同一天
if (dayOfWeek >= 1 && dayOfWeek <= 5) { // 周一至周五
if (hour1 >= 17 && hour1 <= 20 && hour2 >= 17 && hour2 <= 20) { // 晚上
return true;
} else if (hour1 >= 10 && hour1 <= 14 && hour2 >= 10 && hour2 <= 14) { // 中午
return true;
} else {
return false;
}
} else if (dayOfWeek >= 6 && dayOfWeek <= 7) { // 周末
if (getTime(hour1, hour2, minute1, minute2, second1, sceond2) < 3600) { // 一小时内
return true;
} else {
return false;
}
}
} else { // 不是同一天
return false;
}
return false;
}
public long getTime(int hour1, int hour2, int minute1, int minute2, int second1, int second2) {
// 计算时间差,精确到秒
long sum1 = hour1 * 3600 + minute1 * 60 + second1;
long sum2 = hour2 * 3600 + minute2 * 60 + second2;
return Math.abs(sum1 - sum2);
}
public void showTables() {
for (int i = 0; i < count; i++) {
table[i].order.showSentence();
}
for (int i = 0; i < count; i++) {
System.out.println("table " + table[i].tableNum + ": " + table[i].getOriginalPrice() + " "
+ table[i].getDiscountPrice());
}
}
}
踩坑心得
OOP训练集04 7-2有重复数据
题目:在一大堆数据中找出重复的是一件经常要做的事情。现在,我们要处理许多整数,在这些整数中,可能存在重复的数据。
你要写一个程序来做这件事情,读入数据,检查是否有重复的数据。如果有,输出“YES”这三个字母;如果没有,则输出“NO”。
import java.util.Scanner;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
boolean flag = false;
int[] array = new int[n];
for (int i = 0; i < n; i++) {
array[i] = sc.nextInt();
}
Arrays.sort(array);
for (int i = 0; i < n - 1; i++) {
if (array[i] == array[i + 1]) {
flag = true;
break;
}
}
if (flag == true) {
System.out.println("YES");
} else {
System.out.println("NO");
}
sc.close();
}
}
虽然说做对了,但想试试上课老师介绍过ArrayList和Collections方法
import java.util.Scanner;
import java.util.ArrayList;
import java.util.Collections;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
boolean flag = false;
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
arrayList.add(sc.nextInt());
}
Collections.sort(arrayList);
for (int i = 0; i < n - 1; i++) {
System.out.println(arrayList.get(i));
System.out.println(arrayList.get(i + 1));
if (arrayList.get(i) == arrayList.get(i + 1)) {
flag = true;
break;
}
}
if (flag == true) {
System.out.println("YES");
} else {
System.out.println("NO");
}
sc.close();
}
}
踩坑与心得:运行超时,这很符合ArrayList慢的特性
OOP训练集04 7-3 去掉重复的数据
题目:在一大堆数据中找出重复的是一件经常要做的事情。现在,我们要处理许多整数,在这些整数中,可能存在重复的数据。
你要写一个程序来做这件事情,读入数据,检查是否有重复的数据。如果有,去掉所有重复的数字。最后按照输入顺序输出没有重复数字的数据。所有重复的数字只保留第一次出现的那份。
import java.util.HashSet;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int cnt = 0;
HashSet<Integer> seen = new HashSet<Integer>();
for (int i = 0; i < n; i++) {
boolean flag = seen.add(sc.nextInt());
if (flag == true) {
cnt++;
}
}
for (int num : seen) {
System.out.print(num);
if (cnt-- != 1) {
System.out.print(" ");
}
}
sc.close();
}
}
踩坑与心得:使用HashsetSet确实会慢一些导致运行超时,但答案错误却找不出原因。
询问同学后,用StringBuilder来储存该字符串,最后输出即可
import java.util.HashSet;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
StringBuilder sb = new StringBuilder();
HashSet<Integer> seen = new HashSet<Integer>();
for (int i = 0; i < n; i++) {
int num = sc.nextInt();
boolean flag = seen.add(num);
if (flag == true) {
sb.append(num).append(" ");
}
}
sb.deleteCharAt(sb.length() - 1);
String str = sb.toString();
System.out.println(str);
sc.close();
}
}
OOP训练集04 7-7 判断两个日期的先后,计算间隔天数、周数
题目:从键盘输入两个日期,格式如:2022-06-18。判断两个日期的先后,并输出它们之间间隔的天数、周数(不足一周按0计算)。
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String dateString1 = sc.nextLine();
LocalDate date1 = LocalDate.parse(dateString1);
String dateString2 = sc.nextLine();
LocalDate date2 = LocalDate.parse(dateString2);
if (date1.isBefore(date2)) {
System.out.println("第一个日期比第二个日期更早");
} else if (date1.isAfter(date2)) {
System.out.println("第一个日期比第二个日期更晚");
}
long daysBetween = Math.abs(date1.until(date2, ChronoUnit.DAYS));
System.out.println("两个日期间隔" + daysBetween + "天");
long weeksBetween = (long) (daysBetween / 7.0);
System.out.println("两个日期间隔" + weeksBetween + "周");
sc.close();
}
}
踩坑与心得:输入的日期不符合yyyy-mm-dd时,报错
自定义一个名为trans的方法将输入的日期格式转换为yyyy-mm-dd即可
public static String trans(String str) { // 转换日期标准格式
String[] parts = str.split("-");
StringBuilder sb = new StringBuilder();
sb.append(parts[0]);
sb.append("-");
if (parts[1].length() == 1) {
sb.append("0");
}
sb.append(parts[1]);
sb.append("-");
if (parts[2].length() == 1) {
sb.append("0");
}
sb.append(parts[2]);
return sb.toString();
}
OOP训练集05 7-4 正则表达式训练-学号校验
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
String regex = "2020(1[1-7]|61|7[1-3]|8[1-2])([0-3][0-9]|40)";
if (str.matches(regex)) {
System.out.println("正确");
} else {
System.out.println("错误");
}
sc.close();
}
}
踩坑与心得:学号(序号)包含了00,导致错误,修改正则表达式为"2020(1[1-7]|61|7[1-3]|8[1-2])(0[1-9]|[1-3][0-9]|40"即可限定学号为01~40
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
String regex = "2020(1[1-7]|61|7[1-3]|8[1-2])(0[1-9]|[1-3][0-9]|40)";
if (str.matches(regex)) {
System.out.println("正确");
} else {
System.out.println("错误");
}
sc.close();
}
}
改进建议
- 在Array、ArrayList、LinkedList的使用上要有所选择。
- 长度确定且数据较小时使用Array,速度快,占内存小,操作简单;长度不确定且对元素的访问需求较多是,使用ArrayList,访问元素较快且ArrayList会自动扩容防止越界的情况出现;当对数据进行插入、删除操作需求较多时,使用LinkedList,插入、删除的速度更快,由于每一个节点都要储存引用,耗内存的缺点也很明显。
- Array是一个固定大小的容器,底层采用的是线性连续空间来存放元素。优点在于在内存中是连续的,速度较快,操作简单。缺点是定义数组时要定义其长度,不是很灵活,过长过短都会造成问题。不方便进行数据的添加、插入和移除。
- ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。
- 与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。
- 相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
- LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
- 需要输出一行很多个数据时,不使用循环一个一个输出,而直接使用StringBuilder不断append进行拼接。
- 因为每次虚拟机碰到"+"这个操作符对字符串进行拼接地时候会new出一个StringBuilder,然后调用append方法,最后调用toString()方法转换字符串赋值给对象,所以循环多少次,就会new出多少个StringBuileder()来,这对于内存是一种浪费。
总结
- 这三次题目集,学了
- Array、ArrayList、LinkedList的使用,
- Hashset的使用
- LocalDate类中of()、isAfter()、isBefore()、until()等方法的使用规则
- ChronoUnit类中DAYS、WEEKS、MONTHS等单位的用法,正则表达式的使用
- 正则表达式的使用
- 面向对象编程的封装性
- 面向对象设计的聚合关系
- 最大的收获是坚韧不拔的精神
- 建议
- 提高作业难度时勿跨度太大,可以先练习菜单计价程序-4系列前的PTA题目,而且此系列的题目描述相关联,能更容易理解题目描述意思,而且难度上升跨度不会太大。
- 题目集练习的知识点落后于上课内容,希望练习能跟上上课内容,更好地练习巩固学到的知识。