oop第一次博客
write_by_23201707_gongjunjie
oop第一次博客
一:前言
对于java我是比较陌生的,上学期只学了一门C语言,所以java的语法比较陌生,但是经过一段时间的练习,语法上变得稍微熟练
面向对象程序设计的三大特性分别是:封装、继承、多态,前三次pta只涉及到了封装性,后期会加上后两个特性
二:关于三次pta作业
题目是关于判题程序的
知识点:
1.需要进行类的设计,对数组进行封装,要考虑类间关系
2.此次pta使用了正则表达式匹配
3.需要保证类的单一职责原则
4.达到开闭原则,以至于后面迭代的时候可以通过加类而不是疯狂改代码
5.后面几题还会用到list
题量:
1.前两次的题目量本人觉得尚可应对,因为测试点较为简单,但是第三次本人用了很长时间去过测试点,主要是测试点比较难猜到
2.据我所知很多人都是正则表达式不太对导致测试点过不了
难度:
本人认为前两次的难度是中等,第三次我加了很多类,导致代码量较大,后面过测试点的时候比较折磨,但是经过几天的奋斗也是把测试点都过了
三:关于设计与分析
第一次pta作业
关于类设计
第一次作业的类设计较为简单
1.Topic题目类
2.Answer答案类
3.Paper试卷类
4.Main主类
分析代码
1.第一次pta作业当时没有考虑耦合度问题,所以用了关联类,且一些方法的位置放置不太合适,比如输出的方法我放在了Paper类中,个人感觉应该设计一个类专门用来搞类的联系和一些操作,例如输出
通过类图可以看出类间关系还是比较简单的,主要就是Paper将Answer和Topic关联,输出等方法也是放在了Paper类中
下面是SourceMonitor的测试结果
可以看出,由于第一次题目较为简单,所以圈复杂度
2.知道如何使用正则表达式去提取想要的内容,例如在如下信息中提取有用内容
该如何将N中有用的信息提取出来呢?答案是
先把每一句话使用String数组存储,后面对每一句话进行匹配分组,再分别放到自己想放置的类对象的属性中
踩坑心得
类设计不合理,导致后面迭代对代码进行了修改,不符合开闭原则,让代码的可复用性和可变性很低
改进建议
1.应该对类的设计再进一步优化,主类中的东西尽量只是调用方法,这样设计对于后面题目迭代可以有帮助,不至于大改特改
2.对于数组应该使用ArryList数组或者LinkedList来存储信息,好处是可以不用去设置变量来统计有多少信息,后期对于输出时有很大帮助
关于第一次pta作业的分析就到这里,值得一提的是室友这题没有类的设计而是使用Main类完成了题目,老师还说有人没有使用正则表达式而是使用if对信息进行提取
第二次pta作业
关于类设计
1.Content题目类
2.Paper试卷类
3.Answer答卷类
4.Main主类
分析代码
1.对于正则表达式,与上一题差不多,都是用正则表达式对题目进行分组提取有用的部分,但是这次较为不同的是试卷和答卷中的信息不确定,所以正则表达式有所改变,比如:
想要匹配T中的题目编号及分值,但是不知道个数,所以我使用了两次搜索,从而达到目的
点击查看代码
Pattern pattern1 = Pattern.compile("#T:\\s*(\\d+).+");
Matcher matcher1 = pattern1.matcher(all);
Paper[] paper = new Paper[100];
for(int i=0;i<100;i++){
paper[i]=new Paper();
}
int c=0;
int[] a=new int[100];
int d=0;
while(matcher1.find()){//先找到#T这句话
String[]temp = new String[100];
String[]temp1 = new String[100];
d=0;
Pattern pattern2 = Pattern.compile("(\\d+)-(\\d+)");
Matcher matcher2 = pattern2.matcher(matcher1.group(0));//在#T这句话中再次进行匹配,寻找后面想要的部分
while(matcher2.find()){
temp[d]=matcher2.group(1);
temp1[d]=matcher2.group(2);
d++;
a[Integer.parseInt(matcher1.group(1))]++;
}
paper[c]=new Paper(matcher1.group(1),content,temp,temp1);
c++;
}
对于#S的信息提取也是同理
2.关于耦合度,这次依然使用了关联类,所以其他类之间耦合度较高
可以看到将输出等多个方法都放在了Paper类中,是不合理的,后期应该将方法分别放到其他类中
由于产生了迭代,加了一直方法,导致圈复杂度较上一题要大
3.关于第二次pta作业的一个小题目,那个题提到了接口,于是我就去查了一下,发现接口可以弥补继承无法多继承的缺陷
踩坑心得
1.对于题目的理解出了一点偏差,导致有一个测试点一直过不去,题目是如果答卷没有对应的试卷输出“The test paper number does not exist”,但是我理解成了试卷找不到答卷输出这句话,导致最后一个测试点一直过不去,难受啊
改之前是这样
改之后的代码是这样
2.对于题目,我一开始是建立数组,让数组中的信息存在相应序号的一个数组元素中,导致老是报NullPoint,而且会使后期的输出变得非常麻烦,所以最终决定重新写,也就成了现在的版本
之前是把序号当数组下标使用,所以会有空数组出现
3.建议写题目应该先画一个类图,把类之间的关系理清楚,不然写的时候会很混乱,使代码变得复杂
改进建议
1.对于第二次的代码总体觉得尚可,下次的题目可以在这次的基础上进行类的增加,前面的逻辑依然是正确的
2.在类的设计上依然有不足,Paper类不应该与其他类相关联,应该重新设计一个类使各个类之间产生联系
第三次pta作业
第二个小题
关于次此次作业的第二个小题,我觉得还是可以说一下,关于日期类的使用,倒是一个好东西,可以帮助我们快速处理日期问题,关于使用如下:
先用LocalDate定义一个对象,之后就可以使用其方法,如果想要知道日期是一年中的第几天可以用getDayOfYear方法,月中第几天用的是getDayOfMonth,一周中的第几天用的是getDayOfWeek方法,但是要注意的是,getDayOfWeek返回的是星期几的英文,所以需要使用getValue转化成数字,还可以使用date1.until(date2, ChronoUnit.DAYS)来求date1和date2相差多少天,大概用到的就这些方法,在此简述一下
关于类设计
1.Answer答卷类
class Answer {//答卷类
private int paperNum;//答卷号
private Answer1[] answer = new Answer1[100];//答卷中的答案
private String studentID;//答卷学生的学号
}
2.Answer1答案类(用这个名字纯属因为Answer前面被答卷类占用了)
class Answer1 {//答案类
private String num;//答案序号
private String answer;//答案内容
}
3.Content题目类
class Content {//题目类
private String num;//题目序号
private String content;//题目内容
private String standardAnswer;//标准答案
private boolean isDelete = true;//判断此题目是否被删除
}
4.Control控制类
class Control {//控制类,将类之间建立联系
private Content[] content = new Content[100];//题目
private Answer[] answer = new Answer[100];//答卷
private Paper[] paper = new Paper[100];//试卷
private Student[] student = new Student[100];//学生
private Delete[] delete = new Delete[100];//删除信息
}
其中有一些重要的方法
点击查看代码
public void sumGrade(int j) {//判读试卷中总分是否有100分
int sum = 0;
for (int i = 0; i < 100; i++) {
if (paper[j].getContentPoint()[i] != null) {
sum = Integer.parseInt(paper[j].getContentPoint()[i]) + sum;
} else {
break;
}
}
if (sum != 100) {
System.out.println("alert: full score of test paper" + paper[j].getNum() + " is not 100 points");
}
}
public void println(int g, int j, int a, int e) {//输出函数,主要逻辑在这
int f = 0;
int[] flag = new int[100];
int sum = 0;
for (int i = 0; i < a; i++) {
f = 0;
for (int h = 0; h < 100; h++) {
if (answer[j].getAnswer()[h] != null) {
if (Integer.parseInt(answer[j].getAnswer()[h].getNum()) == i + 1) {
f = 1;
break;
}
}
}
if (f == 0) {
System.out.println("answer is null");
} else {
if (content[Integer.parseInt(paper[g].getContentNum()[i])] == null) {
System.out.println("non-existent question~0");
} else {
if (content[Integer.parseInt(paper[g].getContentNum()[i])].isDelete() == false) {
System.out.println(
"the question " + Integer.parseInt(paper[g].getContentNum()[i]) + " invalid~0");
} else {
for (int k = 0; k < 100; k++) {
if (answer[j].getAnswer()[k] != null) {
if (Integer.parseInt(answer[j].getAnswer()[k].getNum()) == i + 1) {
System.out
.print(content[Integer.parseInt(paper[g].getContentNum()[i])].getContent());
if (answer[j].getAnswer()[k].getAnswer()
.equals(content[Integer.parseInt(paper[g].getContentNum()[i])]
.getStandardAnswer())) {
System.out.println("~" + answer[j].getAnswer()[k].getAnswer() + "~" + "true");
flag[i] = 1;
} else {
System.out.println("~" + answer[j].getAnswer()[k].getAnswer() + "~" + "false");
flag[i] = 0;
}
}
}
}
}
}
}
}
int flag1 = 0;
for (int i = 0; i < e; i++) {
if (answer[j].getStudentID().equals(student[i].getStudentID())) {
flag1 = 1;
System.out.print(student[i].getStudentID() + " " + student[i].getName() + ": ");
}
}
if (flag1 == 1) {
for (int i = 0; i < a; i++) {
if (paper[g].getContentPoint()[i] != null) {
if (i != a - 1) {
if (flag[i] == 1) {
sum += Integer.parseInt(paper[g].getContentPoint()[i]);
System.out.print(paper[g].getContentPoint()[i] + " ");
} else {
System.out.print("0" + " ");
}
} else {
if (flag[i] == 1) {
sum += Integer.parseInt(paper[g].getContentPoint()[i]);
System.out.print(paper[g].getContentPoint()[i]);
} else {
System.out.print("0");
}
}
} else {
System.out.print("0");
}
}
System.out.println("~" + sum);
} else {
System.out.println(answer[j].getStudentID() + " not found");
}
}
public void isWrongFormat(ArrayList<String> all) {//判断输入的格式是否错误
for (int i = 0; i < all.size(); i++) {
Pattern pattern = Pattern.compile(
"^#N:(\\d+) #Q:(.*?) #A:(.*)|^#T:\\s*(\\d+)(\\s*\\d+\\-\\d+)*$|^#S:(\\d+) (#A:\\s*(\\d+)\\s*-\\s*(.*?)\\s*(?=#A:|\\n|$))*|^#X:((\\d+) (\\w+)-*)+|^#D:N-(\\d+)$");
Matcher matcher = pattern.matcher(all.get(i));
if (matcher.find()) {
} else {
System.out.println("wrong format:" + all.get(i));
}
}
}
public void isDelete(int f) {//判断题目是否被删除
for (int i = 0; i < 100; i++) {
if (content[i] != null) {
for (int j = 0; j < f; j++) {
if (delete[j].getContentNum().equals(content[i].getNum())) {
content[i].setDelete(false);
}
}
}
}
}
}
分析代码
1.这次的迭代导致我加了很多的类,使类间关系变得比前两个要复杂很多
此次主要把输出之类的方法单独写了一个类出来,让其他类之间没有关联,用Control类来联系其他的类
2.此次的输出逻辑有所变化,上次是答案序号就是试卷中题目出现的顺序,直接和题目相联系,但是这次加上了答案序号,是答案可以乱序,导致输出逻辑有所变化
3.这次的输出还有一个优先级的问题,当一道题目同时出现答案不存在、引用错误题号、题目被删除,只提示一种信息,答案不存在的优先级最高,所以我就将答案不存在当做第一输出,若答案存在,再去判断错误题号和题目被删除
先输出Answer is null!,之后在判断其他
4.这次的输入和上次的没有很大的区别,但是添加了错误格式判断,让我措手不及,由于对正则表达式认识不足,导致改了好几天,还是一份没涨的 奇迹 有趣事件
5.下面是SourceMonitor的测试结果
可以看到圈复杂度要比上面两题大很多,这次迭代的代码量也较上两题有很大提升
踩坑心得
1.关于这次的题目踩坑心得,我认为首当其冲的就是正则表达式上的坑点,毕竟可以改了很久,一直以为是逻辑有不足,结果改到最后也基本都是正则表达式上的问题
public void isWrongFormat(ArrayList<String> all) {//判断输入的格式是否错误
for (int i = 0; i < all.size(); i++) {
Pattern pattern = Pattern.compile(
"^#N:(\\d+) #Q:(.*?) #A:(.*)|^#T:\\s*(\\d+)(\\s*\\d+\\-\\d+)*$|^#S:(\\d+) (#A:\\s*(\\d+)\\s*-\\s*(.*?)\\s*(?=#A:|\\n|$))*|^#X:((\\d+) (\\w+)-*)+|^#D:N-(\\d+)$");
Matcher matcher = pattern.matcher(all.get(i));
if (matcher.find()) {
} else {
System.out.println("wrong format:" + all.get(i));
}
}
}
2.关于正则表达式的坑,首先由于题目需要判断错误格式,有的错误格式是在输出前加了一些东西,而针对这个错误格式我是使用了开始符号,“^”,可以达到以想要的格式输入和判断错误格式,有个同学用的是长度判断,感觉这个好像也挺好
但是这个就让我之前的逻辑出了大问题,因为我以前的逻辑是先把每一句话都存在了一个字符串里,导致加了开始符会找不到正确的格式,所以后面又把输入给改了(悲)
3.对于输入时每一句输入的存储,我一开始是使用了普通的数组,导致每一种类型的输入我都要去搞一个变量来计数,去当后面循环遍历输出时循环的限制条件,所以后面又进行了一次小改动,将一些数组改成了动态数组,这样就可以调用其size方法来获得后期输出的循环限制条件
4.关于一些测试点的坑,空白试卷,一开始我的正则表达式对此判断的是错误格式
之后对于正则表达式进行了修改
还有后面的答案为空字符测试点,一直过不了,之后又学习了关于正则表达式的前瞻断言和非贪婪模式搜索,将这个测试点过了
后面还有一个比较坑的点是题目竟然可以不只算数题,标准答案也可以为空或者是一句话例如go to,所以对标准答案的正则表达式也有问题,之后又进行修改
也是终于得到满分
改进建议
1.关于对有用信息的提取部分,不应该直接在主类中进行,后面要建立一个类将其封装,使后面加上继承和多态的时候不至于代码全错
2.关于输出函数和试卷是否满分等方法个人认为只有一个Control来做依然不是很好,让代码的复用性很低,后期应对其进行修改
总结
学习到的内容
1.正则表达式
关于正则表达式方面我认为收获还是很大的,正则表达式可以对有用信息进行提取,而本次题集我主要是使用了对匹配信息进行分组来达到提取有用信息进行输入的目的,有同学用的是split对信息进行切割,感觉两种方法都挺好,都可以对有用信息进行提取,还有正则表达式的开始符号,不能将信息都放在一个字符串中使用,不然就会出错,正则表达式的非贪婪匹配(.*?)和前瞻断言(?=),可以解决空字符的问题,这两个东西都非常好用,但是对于正则表达式还是有很多东西不够熟练,希望在后期的训练中一点一点积累,最终能熟练掌握正则表达式
2.类设计
这几次的题目集都是在训练我们的类设计能力,但是主要还是考察了类的单一职责原则,就是让类的功能单一化,提高代码的可复用性,为后续迭代做准备,经过三次的pta练习对于类的单一职责我有了初步的认识,但是这三次的类设计并不够好,需要进行更改,为下一次的迭代做准备
总的来说,个人认为老师们的教学方法还是很好的,可以学到很多东西,虽然过程比较艰辛,但是收获很多,可以学到很多知识
关于oop的第一次blog就到此结束
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~