目录:
一.前言
二.设计与分析
三.踩坑心得
四.改进建议
五.总结
一.前言:
一开始接触的就是C语言,而后是Java。两者有相通的也有不同的,C语言面向过程,而JAVA则是面向对象。感觉JAVA的面向对象和struct有点像。
第一次作业偏简单,重点在与正则表达式;第二次作业输出的形式更复杂了;第三次作业增加了一个类并且测试点也更多了,难度逐步递增,代码的长度也逐步递增;
通过前三次的作业,对JAVA也有了表层的理解。而这三次大作业我觉得最难的就是正则表达式的使用,怎么在一串信息中提取出自己想要的,其次是怎么设计好类,类的属性方法和类和类的关系。最后也就是在设计,书写程序是要想到的特殊情况即bug,感觉是要把客户当成小孩,哪怕他们乱输一通,也要给出相应的提示即解决方法
二.设计与分析:
(因第三次大作业是在第一二次的基础上增加难度,所以我这里就设计第三次大作业的类)
题目要求:
可知题目类中包括属性:题号和题目内容和标准答案,行为:判断对错;
class Topic //题目
{
int topicnum;//题号
String topiccontent;//题目内容
String standardanswer;//题目标准答案
public Topic(int topicnum,String topiccontent,String standardanswer)
{
this.topicnum=topicnum;
this.topiccontent=topiccontent;
this.standardanswer=standardanswer;
}
int ifright(String answer)//判断正误
{
if(this.standardanswer.equals(answer))
return 1;
else
return 0;
}
}
试卷中有多个题目以及各个题目的分值(注意:不要混淆顺序号与题号,题号不一定完整且连续)所以我设计了两个属性,一个是题号集合,一个是分值集合,两两对应,属性还包括试卷号,题目集合,试卷的满分和试卷上删除的题号(题目中有删除题号的寻求),行为则是判断正误,匹配题目和计算试卷满分。(这里在设计题目类和试卷类有个不妥当的地方,在后面的改进建议中会提到)
class Paper //试卷
{
int papernum;//试卷号
int papertopicsum;//试卷中题目的数目
Topic[] topic=new Topic[100];//试卷上的题目
int[] questionnum=new int[100];//记录的题号
int[] questionscore=new int[100];//记录的题目分数
int papertopicsumscore=0;//试卷的满分
int deletetopic;//试卷上删除的题号
public Paper(int papernum,int papersum)
{
this.papertopicsum=papersum;
this.papernum=papernum;
}
void matchquestion(int i,int questionnum,int quetionscore)
{
this.questionnum[i]=questionnum;
this.questionscore[i]=quetionscore;
}
int ifcorrect(int num,String answer)//num表示题号//判断num题是否正确
{
int i;
int n=this.topic.length;
for(i=0;i<n;i++)
{
if(topic[i].topicnum==num)
{
return topic[i].ifright(answer);
}
}
return -1;
}
int calculatescoresum()//计算这张试卷的总分
{
int i;
for(i=0;i<papertopicsum;i++)
{
if(this.topic[i].topicnum>0)
papertopicsumscore+=questionscore[i];
}
return papertopicsumscore;
}
}
一张答卷对应一张试卷(注意后面也要判断是否有这么一张试卷能和答卷匹配的上)那么答卷类和试卷类是有联系的,答案类中存考生的答案要与试卷中的标准答案对照判断正误。
class Answerpaper //答卷
{
int answerpapernum;//答卷的编号
Paper paper;//答卷对应的试卷
String answerstudentid;
int answersum;//回答的题目数目
int[] answernum=new int[100];//回答的题目题号
String[] answer=new String[100];//回答的答案
int getscore=0;
boolean[] result=new boolean[100];//题目顺序是试卷的顺序
public Answerpaper(int answerpapernum,String answerstudentid)
{
this.answerpapernum=answerpapernum;
this.answerstudentid=answerstudentid;
}
void matchpaper(Paper paper)
{
this.paper=paper;
}
void matchanswernum(int answersum)
{
this.answersum=answersum;
}
int matchstudent(Student[] student)//判断答卷上的学号是否存在
{
int i;
int n=student.length;
for(i=0;i<n;i++)
{
if(student[i].studentid.equals(this.answerstudentid))
return 1;
}
return 0;
}
void matchanswer(int i,int questionnum,String answer)
{
this.answernum[i]=questionnum;
this.answer[i]=answer;
}
void scoreget()//判断答卷上的回答是否正确
{
int i,j;
int flag=0;//判断答卷中是否没有试卷中的题目
//System.out.printf("试卷中的题目数目:%d-答卷中的题目数目:%d\n",this.paper.papertopicsum,this.answersum);
for(i=0;i<this.paper.papertopicsum;i++)
{
flag=0;
for(j=0;j<this.answersum;j++)
{
if(this.answernum[j]==this.paper.topic[i].topicnum)
{
flag=1;
if(paper.ifcorrect(paper.topic[i].topicnum,answer[j])==1&&!this.paper.topic[i].topiccontent.equals("-1"))
{
result[i]=true;
}
else
{
result[i]=false;
}
}
}
if(flag==0)
{
result[i]=false;
}
}
}
}
三.踩坑心得:
也就是我在前言中提到的,在设计好类和类的关系后,重要的是理清题目中的bug。我在这三次大作业中耗时最长的就是修bug,在开始写代码的时候就自以为是的开始,甚至没看完整题目要求,而要求的改变很可以会产生后面一系列代码的修改。例如第三次作业里,题目,试卷,答卷,和学生的信息是可以打乱的(而我一开始就以样例为默认,试卷的输入是在题目输完以后,而前两次的输入顺序是固定的,就使得第三次的惯性思维),如果试卷的输入是在题目输完以后的,那便可获取到试卷信息的时候就开始匹配相应的题目,而要是乱序的话,就只能用ArrayList将题目,试卷,答卷全部集合好后再一一匹配好。
int x=0;
for(i=1;i<papern;i=i+2)//匹配各个题的分值
{
questionnum[x]=Integer.parseInt(output[i]);
score[x]=Integer.parseInt(output[i+1]);
paper[papersum].matchquestion(x,questionnum[x],score[x]);
x++;
}
那么paper[papersum].matchquestion(x,questionnum[x],score[x]);就应该放在信息全部提取完后。而这种修改让我头痛的就是i的范围。因为直接在
while(true)
{
input[n]=scanner.nextLine();
if(input[n].equals("end"))
{
break;
}
else
{
//提取信息;
}
}
提取信息里匹配的话i的范围就会用到while里的数据,而要在while后匹配,i的范围要重新设置。
开头没开好,没看准需求,最后就只能在一个个测试点上反复折腾
四.改进建议
对我在三次大作业的代码,我觉得有三点是要改进的:
一. 考虑到降低类和类的耦合性,减少类和类的关系(类和类的关系处理不当)。
我一开始的代码是
class Topic//题目
{
int topicnum;
String topiccontent;
String standardanswer;
public Topic(int topicnum,String topiccontent,String standardanswer)
{
this.topicnum=topicnum;
this.topiccontent=topiccontent;
this.standardanswer=standardanswer;
}
}
class Paper//试卷
{
int papernum;
int papertopicsum;//试卷中题目的数目
ArrayList
ArrayList
ArrayList
int papertopicsumscore=0;//试卷的满分
int deletetopic;//试卷上删除的题号
public Paper(int papernum,int papersum)
{
this.papertopicsum=papersum;
this.papernum=papernum;
}
}
在Paper类中的属性包含了Topic类,未降低耦合性可以再设计一个类来将两类联系起来;
class Topic//题目
{
int topicnum;
String topiccontent;
String standardanswer;
public Topic(int topicnum,String topiccontent,String standardanswer)
{
this.topicnum=topicnum;
this.topiccontent=topiccontent;
this.standardanswer=standardanswer;
}
}
class Paper//试卷
{
int papernum;
int papertopicsum;//试卷中题目的数目
ArrayList
ArrayList
int papertopicsumscore=0;//试卷的满分
int deletetopic;//试卷上删除的题号
public Paper(int papernum,int papersum)
{
this.papertopicsum=papersum;
this.papernum=papernum;
}
}
class Connect
{
ArrayList
Paper paper;
二.正如设计和分析模块上说的,我把题目的分值放在了试卷类里,这点是错误的,分值是属于题目的应该放在题目类中,一个类对应一个类的职责(类设计不得当)
三.在上面的代码可以看出我在集合中还是习惯性的用数组,而这对后续的使用会很麻烦(依赖数组)
例如:int[] answernum=new int[100];其中真正储存的有用的信息并不是answernum.length(其值为100),而用ArrayList
五.总结:
以下是我在这三次作业中学到的:
第一次作业:
1、使用Pattern和Matcher,正则表达式提取信息;
第二次作业:
1、ArrayList的使用,split的使用;
第三次作业:
1、StringTokenizer的使用;
三次作业中最主要的就是提取信息,以下我总结出三种方法:
方法一.Pattern,Matcher和正则表达式
代码:
package text;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test
{
public static void main(String[] args)
{
String input="#N:3 #Q:3+2= #A:5";
Pattern pattern=Pattern.compile("#N:(.) #Q:(.) #A:(.*)");
Matcher matcher=pattern.matcher(input);
if(matcher.find())
{
System.out.printf("%s\n",matcher.group(0));
System.out.printf("%s\n",matcher.group(1));
System.out.printf("%s\n",matcher.group(2));
System.out.printf("%s\n",matcher.group(3));
}
}
}
运行结果:
N:3 #Q:3+2= #A:5
3
3+2=
5
方法二:spil和正则表达式
代码:
package text;
public class Test
{
public static void main(String[] args)
{
String input="#N:3 #Q:3+2= #A:5";
String[] output=input.split("[#N:]|[#Q:]|[#A:]|[\s:]");
for(int i=0;i<output.length;i++)
{
if(output[i]!="")
System.out.printf("%s\n",output[i]);
}
}
}
运行结果:
3
3+2=
5
方法三:StringTokenizer
代码:
package text;
import java.util.*;
public class Test
{
public static void main(String[] args)
{
String input="#N:3 #Q:3+2= #A:5";
StringTokenizer st=new StringTokenizer(input,"#ANQ: ");//切割字符串
while(st.hasMoreTokens())
{
System.out.printf("%s\n",st.nextToken());
}
}
}
调试结果:
3
3+2=
5
判断输入是否符合标准:
String input=scanner.nextLine();
String regex="//正则表达式";
boolean isValid=Pattern.matches(regex,input);