PTA题目集阶段总结(2)
一、前言
OOP训练集04:
7题,第1题难度较大,第7题考查了较多的知识,也比较难;主要考察了Set、List和Java三大特性之一的封装性,并教我们学着查询Java API文档,了解Scanner类、String类、Integer类、LocalDate类、ChronoUnit类中各种单位的用法和规则。
OOP训练集05:
6题,难度一般;主要考察了正则表达式和日期类聚合。
OOP训练集06:
5题,第3题和第5题难度较大;主要考察了Java的另外两大特性之继承和多态、Java 中的字符串处理类以及正则表达式,最重要的是,本次题目集的第4、5题,没有给出相应的类图,考查了我们的程序设计能力。
二、设计与分析
OOP训练集04 7-1
这题我没有写出来,参考其他人的源码得到了如下的类图,然后我大概明白了老师说的不符合结构的源码是长什么样子的:
1.本题输入比较复杂,根据题目提供的一些类一层层来写 ;
2.可以使用静态类来写。
OOP训练集05 7-5 日期问题面向对象设计(聚合一)
本题给出了类图,按照类图对原来的代码进行一些改正就行,不过需要注意几点问题,这会在第三部分一一说明。
在这里贴出部分源码,分别是对应类图写的年、月、日类:
class Year {
private int value;
public Year() {}
public Year(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean isLeapYear() {
if ((value % 4 == 0 && value % 100 != 0) || value % 400 == 0)
return true;
return false;
}
public boolean validate() {
if (value >= 1900 && value <= 2050)
return true;
return false;
}
public void yearIncrement() {
value += 1;
}
public void yearReduction() {
value -= 1;
}
}
class Month {
private int value;
Year year;
public Month() {}
public Month(int yearValue, int monthValue) {
// year.setValue(yearValue);
// 调用有参的public Year(int value)方法
this.year = new Year(yearValue);
this.value = monthValue;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Year getYear() {
return year;
//return year.getValue();
}
public void setYear(Year year) {
//year.setValue(value);
this.year = year;
}
public void resetMin() {
value = 1;
}
public void resetMax() {
value = 12;
}
public boolean validate() {
if (value >= 1 && value <= 12)
return true;
return false;
}
public void monthIncrement() {
value += 1;
}
public void monthReduction() {
value -= 1;
}
}
class Day {
private int value;
Month month;
private int[] mon_maxnum = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public Day() {}
public Day(int yearValue, int monthValue, int dayValue) {
// 调用有参的public Month(int yearValue, int monthValue)方法
this.month = new Month(yearValue, monthValue);
this.value = dayValue;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Month getMonth() {
return month;
}
public void setMonth(Month value) {
// 注意,这里和public void setYear(Year year)不同
// 这里是Month value,不是Month month
this.month = value;
}
public void resetMin() {
this.value = 1;
}
public void resetMax() {
// Month中的value是私有属性,不能直接调用
// 需要借助public getValue()方法
int currentMonth = month.getValue();
value = mon_maxnum[currentMonth-1];
}
public boolean validate() {
// 不要忘记闰年的情况
if (month.getYear().isLeapYear() == true)
// 2月对应的下标是1
mon_maxnum[1] = 29;
// 由resetMax可知,这里也需要借助public getValue()方法
if (value >= 1 && value <= mon_maxnum[month.getValue()-1])
return true;
return false;
}
public void dayIncrement() {
value += 1;
}
public void dayReduction() {
value -= 1;
}
}
OOP训练集05 7-6 日期问题面向对象设计(聚合二)
这一题的类与上一题不同,这里贴出不同之处:
class Year {
private int value;
}
class Month {
private int value;
public Month(int value) {
this.value = value;
}
}
class Day {
private int value;
public Day(int value) {
this.value = value;
}
}
OOP训练集06 7-4
本题需要自己设计,各个类的关系都要考虑到,这部分比较麻烦,我的类图如下:
在ClientInformation类中使用一个公开的构造方法储存用户信息,这部分用到了类的数组:
public ClientInformation() {
// 每个账户内的卡号
Card[] card1 = {new Card("6217000010041315709", "88888888"), new Card("6217000010041315715", "88888888")};
Card[] card2 = {new Card("6217000010041315718", "88888888")};
Card[] card3 = {new Card("6217000010051320007", "88888888")};
Card[] card4 = {new Card("6222081502001312389", "88888888")};
Card[] card5 = {new Card("6222081502001312390", "88888888")};
Card[] card6 = {new Card("6222081502001312399", "88888888"), new Card("6222081502001312400", "88888888")};
Card[] card7 = {new Card("6222081502051320785", "88888888")};
Card[] card8 = {new Card("6222081502051320786", "88888888")};
// 每位客户的账户
Account[] account1 = {new Account("3217000010041315709", card1), new Account("3217000010041315715", card2)};
Account[] account2 = {new Account("3217000010051320007", card3)};
Account[] account3 = {new Account("3217000010051320007", card4), new Account("3222081502001312389", card5), new Account("3217000010041315709", card6)};
Account[] account4 = {new Account("3217000010041315709", card7), new Account("3217000010041315709", card8)};
// 每个银行的用户
User[] user1 = {new User("杨过", account1), new User("郭靖", account2)};
User[] user2 = {new User("张无忌", account3), new User("韦小宝", account4)};
// 每个银行的ATM机
ATM[] atm1 = {new ATM("01"), new ATM("02"), new ATM("03"), new ATM("04")};
ATM[] atm2 = {new ATM("05"), new ATM("06")};
// 银行类(银行名称, 用户, ATM机)
banks[0] = new Bank("中国建设银行", user1, atm1);
banks[1] = new Bank("中国工商银行", user2, atm2);
}
OOP训练集06 7-5
本题在原来的基础上添加了一些信息,但删去了存款功能,只保留了取款和查询功能,这一题的类图如下:
在ClientInformation类中储存用户信息的方法也需要增改,要添加其他用户和银行卡类型public public ClientInformation() {
// 每个账户内的卡号
Card[] card1 = {new Card("6217000010041315709", "88888888"), new Card("6217000010041315715", "88888888")};
Card[] card2 = {new Card("6217000010041315718", "88888888")};
Card[] card3 = {new Card("6217000010051320007", "88888888")};
Card[] card4 = {new Card("6222081502001312389", "88888888")};
Card[] card5 = {new Card("6222081502001312390", "88888888")};
Card[] card6 = {new Card("6222081502001312399", "88888888"), new Card("6222081502001312400", "88888888")};
Card[] card7 = {new Card("6222081502051320785", "88888888")};
Card[] card8 = {new Card("6222081502051320786", "88888888")};
Card[] card9 = {new Card("6640000010045442002", "88888888"), new Card("6640000010045442003", "88888888")};
Card[] card10 = {new Card("6640000010045441009", "88888888")};
Card[] card11 = {new Card("6630000010033431001", "88888888")};
Card[] card12 = {new Card("6630000010033431008", "88888888")};
// 每位客户的账户
Account[] account1 = {new Account("3217000010041315709", "借记账号", card1), new Account("3217000010041315715", "借记账号", card2)};
Account[] account2 = {new Account("3217000010051320007", "借记账号", card3)};
Account[] account3 = {new Account("3217000010051320007", "借记账号", card4), new Account("3222081502001312389", "借记账号", card5),
new Account("3217000010041315709", "借记账号", card6)};
Account[] account4 = {new Account("3217000010041315709", "借记账号", card7), new Account("3217000010041315709", "借记账号", card8)};
Account[] account5 = {new Account("3640000010045442002", "贷记账号", card9)};
Account[] account6 = {new Account("3640000010045441009", "贷记账号", card10)};
Account[] account7 = {new Account("3630000010033431001", "贷记账号", card11)};
Account[] account8 = {new Account("3630000010033431008", "贷记账号", card12)};
// 每个银行的用户
User[] user1 = {new User("杨过", account1), new User("郭靖", account2), new User("张三丰", account5)};
User[] user2 = {new User("张无忌", account3), new User("韦小宝", account4), new User("令狐冲", account6)};
User[] user3 = {new User("乔峰", account7), new User("洪七公", account8)};
// 每个银行的ATM机
ATM[] atm1 = {new ATM("01"), new ATM("02"), new ATM("03"), new ATM("04")};
ATM[] atm2 = {new ATM("05"), new ATM("06")};
ATM[] atm3 = {new ATM("07"), new ATM("08"), new ATM("09"), new ATM("10"), new ATM("11")};
// 银行类(银行名称, 用户, ATM机)
banks[0] = new Bank("中国建设银行", user1, atm1);
banks[1] = new Bank("中国工商银行", user2, atm2);
banks[2] = new Bank("中国农业银行", user3, atm3);
}
三、采坑心得
OOP训练集05 7-5
1.开始写的如下,结果导致一直输出“Wrong Format!”:
后面改成了这样,才能正确运行:
开始没想通为什么会这样,写到后面突然开窍,Day的有参构造方法如下,三个参数按顺序分别是年、月、日:
主函数中创建的DateUtil类中对应的三个参数如下:
对应DateUtil的有参构造方法,我一开始不动脑子地按照类图上给的属性的顺序从上到下写的,就写成了DateUtil(int d, int m, int y),其中d对应的是year,y对应的是day;但是下一行又想着y代表的应该是year,而day代表的是day,所以就写了this.day = new Day(y, m, d),就等于把代表“日”的“y”放在了“年”的位置,把代表年的“d”放在了“日”的位置,所以一直会输出“Wrong Format!”。
2.不能像之前一样直接调用各个类的方法,而需要多次调用,如下:
3.之前是利用“this.isLeapYear(year)”判断每一年是不是闰年,而在这里需要通过“new Year(y).isLeapYear()”将y值作为参数传回去进行闰年的判断;另外需要注意,在判断“下一年”和“前一年”是否是闰年时,传回去的参数分别应该是“y+1”和“y-1”,否则会造成几天的偏差。
4.求两个日期相差的天数的时候判断闰年的方法也有所不同:
5.返回值也需要改动。
6.原来的主函数中输出如下:
但在本程序中会输出一串类似地址值的符号,所以主函数中也要改一些东西:
OOP训练集06 7-4
在设计程序之外,还有几点问题:
1.单例模式没学全,卡在多次存款和取款着两个点上了,一开始写的如下,当时脑袋抽抽地觉得有没有static问题不大,也根本没有调用getInformation,光顾着new ClientInformation()了,当然无法保存之前的存取记录,也无法正常使用查询功能:
正确的单例模式应该如下:
2.用户信息类一开始弄错了,每张卡给安排了一个变量名,每个账户数组中放的是卡的变量名,银行用户数组也给安排了四个,每个数组里面放的是账户的数组名,而银行数组里面放的是用户名的数组名,但这样与前面设计的各类的属性不一致,修改后如下;同时,这就导致了用户信息类中变量名和数组名的意思和含义不对,没有做到见名知义,比如Card[]更应该改成Account[],因为其中放的是每个账户内的卡号,而一个账户可以由几个不同的卡号,若按照名字理解,就要每张卡对应一个Card[];每位客户的账户同理,更应该由Account[]换成User[],而相应地,每个银行的用户应该由User[]换成Bank[]:
3.如上图的各个类的属性设计,应该对应客户信息类将一些属性设计为数组。
OOP训练集06 7-5
1.本题加了许多东西,不过仍然有两个测试点没有过,测试点4和测试点8,其中测试点4需要考虑到超出透支额度的取款时,需要先加上手续费,然后再判断是否透支取款超过5万元。
2.测试点8中在本行和跨行取款是不一样的,需要注意一下。
四、改进建议
1.由第二部分对OOP训练集5 7-5中第一点的分析应该改为:
public DateUtil(int y, int m, int d) {
this.day = new Day(y, m, d);
}
2.在OOP训练集05 7-5中为了省事把计算跨行手续费放在了Check类而不是取款类,这部分应该再改改:
五、总结
这三个题目集比之前三个题目集难度更大,代码也更长,涉及了Java的三大特性,更加考验我们的能力。通过这三次题目集,我得到了不小的长进:
1.掌握了Java的三大特性——封装性、继承和多态;
2.学习了正则表达式的多种用法;
但也存在许多问题:
1.对类的设计比较混乱,这个程序需要设计那些类、每个类里有包含了哪些属性和方法,一开始并没有设计好,到写程序的时候因为功能需要又回去删删改改,导致有些类的属性混乱,有些类其实用不上,有些类又承载了本不属于它的东西,删删改改、缝缝补补一套下来,费时费力且没有得到预期的效果;
2.还不能正则表达式的使用,特别是再水文检测哪一题,需要一点点对着资料写,各种符号数字字母让我很混乱,无法处理。