OO第一阶段习题集总结
一、前言
大一下学期开始接触Java,通过看书、看MOOC课程学得一些Java语法, 因为有C语言的基础,可以很容易地写出一个简单的Java程序并通过编译正常运行。当然免不了嘴角上扬,沾沾自喜,心想:Java不过如此嘛。不过很快被打脸,三次题目集难度一直在上升,在写程序的时候也是困难重重的,遇到解决不了的问题就一直看书,CSDN一通查找,可见要想学好还是要投入时间细细钻研的。经过一个月的Java学习,给我最大的感受就是:一天不去学习Java,第二天就会觉得Java很生疏,所以必须每天坚持去学习实践。
三次题目集涉及的我认为值得注意 的知识点有数组(Arrays类),字符串(String类型):获取字符串长度,从字符串中获取字符,连接字符串,获取字串和字符串比较,方法、对象、类以及面向对象的思考。
题目集一是最简单的入门,一共有八道题,虽然看起来多,但写起来是很快的。没有复杂的算法,只是简单地考察基本的Java语法,而且大部分的题目是在学C语言时写过的,思路都很简单,唯一有卡壳是在7-8 判断三角形类型,第一次做的时候有踩很多坑,但经过很多资料的查找和调试,程序通过了编译和PTA测试。在两天内利用课余零碎的时间全部完成。
题目集二相比题目集一,题量稍少,难度稍有提升,涉及到了数组、字符串与Boolean类型,字符串与数组相比C语言有所差异。做第一道题时陷入了"当局者迷"的状况,因为循环里的一个小错误调试了近3个小时。其中后三道题都是关于日期的题目,第一次做感觉难度很上头。自己一点一点地看教材,通过一些例子启发,历时三天,利用课余零碎时间完成。
题目集三是做的最糟糕的一次,老师在发布作业时已明确说明难度又有了提升,而且Deadline也延长到了10天,但没有引起足够的重视,第三都题没有做的很好。前两道题的所用到的知识点涉及到了类与类的封装性,这两块知识老师在课堂上有所讲解并举了实例,再通过课后看书,虽然做题过程中磕磕绊绊,但最终还是全部调试成功。因为一些课外活动,这周的课余时间不是充裕,前两道题历时三天完成。第三题简单多项式求导是三个题中最复杂的一个,涉及到了正则表达式,需要自学,对输入的字符串用正则表达式对式子进行匹配替换操作时困难重重,输出信息与要求不符合……最终失去耐心删掉了第一次做的源码,开始重新做,但在题目集时间截至时没有能够全部完成。至此反思,对于题目集三的时间投入还是不够多,不够耐心去钻研思考,容易烦躁。接下来的作业要吸取此次教训,全力以赴。
二、设计与分析:
题目集一
1-7题都是入门级的难度,而且在学C语言的时候有做过,一次性通过编译,经过简单的调试通过PTA的检测。但由于还是沿用C语言的思维模式,部分源码中包含了大量的if判断语句及其嵌套,使得其复杂度上升,如2,4,8题。

第二题:7-2 电话键盘字母数字转换(最高复杂度54)
源码:
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String n = input.nextLine();
char i = n.charAt(0);
if(i == 'a'||i == 'b'||i == 'c'||i == 'A'||i == 'B'||i =='C')
System.out.println("2");
else if(i == 'd'||i == 'e'||i == 'f'||i == 'D'||i == 'E'||i == 'F')
System.out.println("3");
else if(i == 'g'||i == 'h'||i == 'i'||i == 'G'||i == 'H'||i == 'I')
System.out.println("4");
else if(i == 'j'||i == 'k'||i == 'l'||i == 'J'||i == 'K'||i == 'L')
System.out.println("5");
else if(i == 'm'||i == 'n'||i == 'o'||i == 'M'||i == 'N'||i == 'O')
System.out.println("6");
else if(i == 'p'||i == 'q'||i == 'r'||i == 's'||i == 'P'||i == 'Q'||i == 'R'||i == 'S')
System.out.println("7");
else if(i == 't'||i == 'u'||i == 'v'||i == 'T'||i == 'U'||i =='V')
System.out.println("8");
else if(i == 'w'||i == 'x'||i == 'y'||i == 'z'||i == 'W'||i == 'X'||i == 'Y'||i == 'Z')
System.out.println("9");
else
System.out.println(i+" is an invalid input");
}
}

分析:
这一题完完全全是学C语言时的思维模式,通篇if-else语句进行输入判断(惨不忍睹)。输入运用了字符串,用于输入大小写字母,再用.charAt将字符串中的字符提取出来,在if语句里进行单个比较。源码可以运用switch语句进行筛选,然后输出相应的数字,这样可以降低一些圈复杂度。
修改为switch语句后:
import java.util.Scanner;
public class W2 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String n = input.next();
char i = n.charAt(0);
switch (i) {
case 'a':
case 'b':
case 'c':
case 'A':
case 'B':
case 'C':
System.out.println("2");
break;
case 'd':
case 'e':
case 'f':
case 'D':
case 'E':
case 'F':
System.out.println("3");
break;
case 'g':
case 'h':
case 'i':
case 'G':
case 'H':
case 'I':
System.out.println("4");
break;
case 'j':
case 'k':
case 'l':
case 'J':
case 'K':
case 'L':
System.out.println("5");
break;
case 'm':
case 'n':
case 'o':
case 'M':
case 'N':
case 'O':
System.out.println("6");
break;
case 'p':
case 'q':
case 'r':
case 's':
case 'P':
case 'Q':
case 'R':
case 'S':
System.out.println("7");
break;
case 't':
case 'u':
case 'v':
case 'T':
case 'U':
case 'V':
System.out.println("8");
break;
case 'w':
case 'x':
case 'y':
case 'z':
case 'W':
case 'X':
case 'Y':
case 'Z':
System.out.println("9");
break;
default :
System.out.println(i+" is an invalid input");
}
结果:



圈复杂度明显降低(由54降低到了10)
第四题:7-4 计算税率(最高复杂度32)

7-4部分源码

分析:
该题源码在判断每一种用户类型时都用到了if-else语句,其中还嵌套了判断收入的if-else语句,而且计算公式也夹杂在其中,使其复杂度骤增。在判断用户类型的时候可以使用switch语句来减少运算量,税率计算公式可以在if-else语句之前计算好,将其赋值给专门用来表示其意义的变量,以降低源码复杂度。
第八题:7-8 判断三角形类型(最高复杂度27)
源码:
import java.util.Scanner;
public class W8 {
public static void main(String[] args) {
Scanner input = new Scanner (System.in);
double[] a = new double[3];
for(int i = 0 ; i < 3 ; i ++ )
a[i] = input.nextDouble();
double x,y,z;
x = a[0];
y = a[1];
z = a[2];
double i = x * x + y * y - z * z;
double j = x * x + z * z - y * y;
double k = y * y + z * z - x * x;
if(x<1||x>200||y<1||y>200||z<1||z>200)
System.out.println("Wrong Format");
else {
if( x == y && x == z && y == x )
System.out.println("Equilateral triangle");
else if( (x == y && x != z) ||( x == z && x != y ) || ( y == z && y != x) )
{
if( Math.abs(i) < 0.0001 || Math.abs(j) < 0.0001 || Math.abs(k) < 0.0001 )
System.out.println("Isosceles right-angled triangle");
else if( (x + y > z) && (x + z > y) && (y + z > x) )
System.out.println("Isosceles triangle");
else
System.out.println("Not a triangle");
}
else if( Math.abs(i) < 0.0001 || Math.abs(j) < 0.0001 || Math.abs(k) < 0.0001 )
System.out.println("Right-angled triangle");
else if( (x + y > z) && (x + z > y) && (y + z > x) )
System.out.println("General triangle");
else
System.out.println("Not a triangle");
}
结果:


分析:
第八题在根据勾股定理判断直角三角形的计算中,没有考虑到当三角形边长为double类型时,由于double类型的字节长度,两边平方和可能与第三边的平方的差值不为0,造成逻辑判断错误,所以在此节点上花费了大量时间去优化,最终判定当其差值小于0.0001时,判定其为直角三角形。代码中还是if-else语句泛滥,使得圈复杂度偏高。第一次做的时候可能不会想这么多,但现在回头看以前写的代码感觉有好多地方可以去优化修改,也许是当时偷懒,没有尽力去想更好多方法。但在往后的编码中要注意代码复杂度及其质量。
题目集一总结:
尽量少用if-else语句以达到降低圈复杂度,提高代码质量的目的。
写代码时要充分思考,使用更简洁的方法。
题目集二
这次的题集相比题目集一的难度上升了挺多,尤其是后三道有关日期的题目,需要考虑多种情况,以至于全复杂度极高。

第一题:7-1 IP地址转换
源码:
import java.util.Scanner;
public class W1 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int dec1,dec2,dec3,dec4,i,j,num;
dec1 = 0;
dec2 = 0;
dec3 = 0;
dec4 = 0;
j = 0;
// System.out.println("Enter the number:");
String bin = input.nextLine();
long [] bins = new long[32];
if(bin.length() != 32) {
System.out.println("Wrong Format");
System.exit(0);
}
for(i = 0; i < 32; i++) {
if(bins[i] != 0 && bins[i] != 1) {
System.out.println("Wrong Format");
System.exit(0);
}
for(i = 0; i < 32; i++) {
bins[i] = Integer.parseInt(java.lang.String.valueOf(bin.charAt(i)));
}
}
for(i = 7; i >= 0; i--) {
num = (int) (Math.pow(2, i) * bins[j]);
dec1 += num;
j++;
}
System.out.print(dec1 + ".");
for(i = 7; i >= 0; i--) {
num = (int) (Math.pow(2, i) * bins[j]);
dec2 += num;
j++;
}
System.out.print(dec2 + ".");
for(i = 7; i >= 0; i--) {
num = (int) (Math.pow(2, i) * bins[j]);
dec3 += num;
j++;
}
System.out.print(dec3 + ".");
for(i = 7; i >= 0; i --) {
num = (int) (Math.pow(2, i) * bins[j]);
dec4 += num;
j++;
}
System.out.print(dec4);
}
}
结果:


分析:
整体结构较上一题目集规范整洁,对于输入的32位二进制数储存在了字符串中,并用.lehgth判断输入字符串中字符个数。然后利用.charAt将字符从字符串中提取出来存到一个数组中,通过四个for循环每次提取8个二进制数,再通过计算得出3位十进制数输出,得出最终结果。由于只使用了少量的if-else语句,并大量使用了for语句,代码的圈复杂度刚好为10。
第二题:7-2 合并两个有序数组为新的有序数组
源码:
import java.util.Scanner;
public class W2 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int a;
// System.out.println("输入第一个数组大小");
int i = input.nextInt();
int [] m = new int[i];
// System.out.println("输入第一个数组数字";
for(a = 0; a < i; a++)
m[a] = input.nextInt();
// System.out.println("输入第二个数组大小");
int j = input.nextInt();
int [] n = new int[j];
// System.out.println("输入第二个数组数字");
for(a = 0; a < j; a++)
n[a] = input.nextInt();
int [] l = new int[i + j];
int num = 0;
for (a = 0; a < i; a++) {
l[a] = m[a];
num++;
}
for (a = 0; a < j; a++) {
l[num++] = n[a];
}
java.util.Arrays.sort(l);
for(a = 0; a < i + j; a++) {
System.out.print(l[a] + " ");
}
}
}
结果:


分析:
这道题第一次做就感到比较简单,思路清晰,写完代码经过简单调试提交通过PTA测试。按照题目要求获取输入数组的长度和存入其实中的数,然后重新声明一个全新的数组,长度为前两个数组长度之和,再利用for循环分别将前两个数组按顺序赋值给新的数组。最后调用arrays.sort方法将新数组进行排序,再用for循环输出。
接下来的三道题做的有那么点糟糕……
7-3 判断闰年及星期几


7-3部分源码
结果:


7-4 求下一天

结果:


7-5 求前N天

7-5部分代码(使用了巨多的if-else语句,还加嵌套,使圈复杂度骤增)
结果:


分析:
三道题都与日期相关,有一定的相似性,都涉及到大月小月的天数,闰年与非闰年里2月的天数两个关键要素。三道题中题判断闰年(Boolean类型),判断二月天数,判断大小月的天数,相关计算都使用了if-else语句, if-else多次嵌套,这部分判断日期代码几乎一样。虽说写的时候逻辑思路很清晰,但这样写导致代码有好多地方都是重复的,提交PTA检测时也有多次运行超时。记得老师说过,代码有好多地方重复的话说明代码出了问题。第一次做的时候并没有引起重视,想着只要做出来能通过编译通过PTA测试就好了,并没有考虑后面的事情——代码的维护、调试。这三道题的代码在提交前调试虽然不是很难,但耗费了很多时间去逐个排查,修改代码找错误点也非常的费劲。上SourceMonitor一测,直接好家伙,7-5的圈复杂度上到了86,其他两道题的圈复杂度也都不低。归根结底还是做的少,经验不足,思维模式还停留在学C语言的阶段,再加上还有些偷懒,没有深入去思考更简洁的算法,只会用if-else去选择。其实好多if-else可以用switch选择和while循环等代替。接下来我会利用空闲时间去优化重构这部分圈复杂度极高的代码,积累经验。
题目集二总结:
经验少,第一次写新题要花费大量时间去翻书理解题目,效率地下。
面对比较复杂的题目没有去深入理解钻研,想当然写出的代码质量低,容易运行超时。
题目集三
目前为止,感觉这次题目集难度极高,加入了类与类的封装性的概念,刚开始比较难理解。尤其第三题,涉及到了需要自学的正则表达式,第一次没有写好,全盘推掉第一次的源码重新写,浪费了很多时间,导致在Deadline前没有写完,所以说本次题目集做的是很糟糕的。题目集三我特意在控制if-else语句的使用以提高代码质量和效率,虽然第二题最高复杂度到了20,但平均复杂度保持在了3以下。通过题目集三的联系,对类与类的封装性有了更深的理解。

7-1 创建账户类Account

- 7-1代码类图

7-1部分源码
结果:


分析:
题目给了详细且明确的要求,根据要求去创建类和方法,明确需求后很快有了思路,虽然大部分代码为自动生成,但通过这道题我进一步理解了封装性的作用和好处。有一个小难点就是对于题目中要求的"账户开户时间,私有属性,LocalDate类型,默认为2020年7月31日",尝试了多种我已知的方法,但最终的输出结果都是"0-0-0"。通过多方查找资料,设置了"private LocalDate dateCreated = LocalDate.of(2020, 07, 31);"私有属性,在最后可以正确得出"The Account'dateCreated:2020-07-31"的结果。
7-2 定义日期类


7-2类图(我的类图<左>、题目给出的类图<右>)

7-2部分源码

结果:

分析:
根据题目给出的类图创建相应的类和方法,再加上在题目集二中做过求下一天的题目,所以在算法上没有很大难度,最主要是要注意运用类与类的封装性,年月日和月的最大天数都是私有属性,在Date类中不能出现相关变量。这次刻意避免了用if-else来判断日期,使用了switch语句,但还是出现了复杂的嵌套,最高圈复杂度可能就是因为switch的嵌套,但平均圈复杂度没有像题目集二中那么高,现在只有2.92。这斌不是最终结果,后续我还会去优化switch的嵌套部分。最终做出来源码的类图和题目中的全部相同,除了月份最大天数,关于每月的最大天数,我将数组中对应的2月份天数初始化为0,到后面判断闰年的时根据具体概况重新对其进行赋值。

7-3 一元多项式求导(类设计)

7-3完整类图
分析:
说实话,这道题并没有真正的全身心去投入:首先对指导书没有进行一个钻研与深刻的思考,对题目要求一知半解,导致在做的过程中困难重重,做好的代码也是不合要求。可能是被正则表达式所困惑,弄错了侧重点,过程中一直致力于使用正则表达式,自己又把这道题目想的过于复杂了,以至于第一次写的代码到最后自己都看不懂,只好全部推翻重写,浪费了很多时间。时间没有合理的安排,没有在一个整块时间去做,以保证思路的模块性。总是用零零散散的时间去做,每次开始时总会花一段时间去想上一阶段做了什么,思维不连贯,效率低下。
这次的第三题对我来说就是一个教训,磨刀不误砍柴工,做题之前一定先要把题目理解清楚,明确需求;勤快一些,克服拖延症,不懂就问,不会就学。
题目集三总结:
了解了类的封装性并可以熟练地运用类去写代码,规避了前两次题目集的一些不当操作,平均圈复杂度在5以下。
需要且值得反思的是第三题,没能按时完成,接下来利用空闲时间继续完成。
三、踩坑心得:
关于代码调试,我基本上都是在eclipse上测试PTA给出的测试数据,确认无误后再提交至PTA,所以有时可以提交一次便可通过测试。
第一次在PTA提交Java代码时有编译错误,这是因为代码中必须存在一个public class Main,不允许出现其他的public class。将原来的名称改为Main即可通过检测。
源码提交过程中,由于题目集一、二的题目用了大量的if-else嵌套语句,降低了代码的运行效率,出现最多的是运行超时。
出现"答案错误"的一种情况是在编码过程中会忽略题目中的几项需求,导致输出结果与需求结果不符合。遇到这种情况,我经常是在eclipse里找特殊数据进行强测,找出错误段,进行修改补充。另一种情况便是因为输出格式问题:在PTA题目下的测试区进行运行测试,对比正确输出,找出错误,修改代码。
再者就是逻辑错误,在eclipse中调试便能发现问题,比如在题目集二中的第一题二进制转十进制:输入"11000110101001000011011011011001",输出为"255.255.0.255",而正确输出为"198.164.54.217",经过反复检查,发现是在for循环中i的范围取值错误导致输出错误,修改后得正确输出。
对面向对象概念的理解不是很深刻,写出的代码不符合面向对象的规则。
还有很多在做题过程中踩的坑,在分析与设计中叙述了很多,在此不再赘述。
四、改进建议:
对于题目集一、二的一部分改进有在题目集三中体现:尽量减少if-else语句的使用,提高代码质量。
加深对面向对象概念的理解,熟练掌握类、方法和封装性的应用。
勤加练习,深入体会,磨练意志,提高耐性,克服拖延症,合理安排时间,按时完成任务。
五、总结:
总体来说,还是经验不足,知识点没有完全学扎实 ,写出的代码质量不是很高,在写代码的时候还会频繁地去翻书。
时间上,没有找到整块时间去全身心的投入,零零碎碎的时间导致写题思路不连贯,有些好的灵感经常被打断,效率不高。
虽然每周题量不是很多,但明显感觉课余时间都投入到了Java上,题目的难度刚开始做会觉得很难,无从下手,但慢慢去分析题目分析需求还是能找到眉目的。现在回头去看以前做的题目集又是另一个感觉,和当时第一次做的感觉完全不一样。
对于上课,紧跟老师的步伐,把每堂课的笔记空闲时去整理翻看,深入理解体会,自己重新写 一次老师写过的例子。
通过这次blog总结,发现了以前写的代码有好多的不足和可以优化改进的地方,也为下一步的学习指明了方向。

浙公网安备 33010602011771号