一、前言
本次博客主是针对java学习第三阶段中的第6-8次PTA题目(成绩计算系列)的总结性博客,第三阶段的作业难度与第一阶段和第二阶段想必有所提高,对java的知识点考察主要集中在类的设计,正则表达式的运用,类的继承,多态,抽象类与接口。
二、设计与分析
1.课程成绩统计程序-1
1)类图
2)生成报表
(绿色环形区域即被测量维度的期望值,维度上的点则是测量得到的实际值)
据报表分析:
①平均圈复杂度和函数深度超标,圈复杂度大,说明程序代码的判断逻辑复杂,可能质量低且难于测试和维护。
②耦合度过高:类与类间分支嵌套过多
3)代码解释:
本题我利用到了继承,与Comparable接口。我创建了课程类,学生类,选课类(将学生和课程联系在一起),成绩类,班级类,输出类。其中因为题干所述选修课和必修课的成绩计算方式不同,所以又设计了考试成绩类和考察成绩类分别继承成绩类。
课程类,学生类都继承了Comparable接口,可以按题干要求实现首字母排序。
我在main里面进行了输入的判断,定义了选课信息,班级信息,学生信息的动态数组,进行完输入后,我再将构造好了的选课信息,班级信息,学生信息数组传到输出类里,进行输出。
4)部分代码如下:
class 课程 implements Comparable<课程>{ String 课程名称; String 课程性质; String 考核方式; public 课程(String 课程名称, String 课程性质, String 考核方式) { super(); this.课程名称 = 课程名称; this.课程性质 = 课程性质; this.考核方式 = 考核方式; } @Override public int compareTo(课程 o) {//按照课程名字排序 // TODO Auto-generated method stub //return 0; String[] arrays=new String[2]; arrays[0]=this.课程名称; arrays[1]=o.课程名称; Comparator com = Collator.getInstance(java.util.Locale.CHINA); Arrays.sort(arrays, com); if(arrays[0].equals(o.课程名称)) { return 1; } if(arrays[0].equals(this.课程名称)) { return -1; } else return 0; /*char[] c_pre = this.课程名称.toCharArray(); char[] c_next = o.课程名称.toCharArray(); int minSize = Math.min(c_pre.length, c_next.length); for (int i = 0; i < minSize; i++) { if((int)c_pre[i] > (int)c_next[i]){ return 1; }else if((int)c_pre[i] < (int)c_next[i]){ return -1; } } if(c_pre.length > c_next.length){ return 1; } return -1;*/ } } class 学生 implements Comparable<学生>{//组合 String 姓名; String 学号; public 学生(String 姓名, String 学号) { super(); this.姓名 = 姓名; this.学号 = 学号; } @Override public int compareTo(学生 o) { // TODO Auto-generated method stub String thisx=this.学号.substring(this.学号.length()-2); String ox=o.学号.substring(o.学号.length()-2); int thisxh=Integer.parseInt(thisx);//this学号 int oxh=Integer.parseInt(ox);//o学号 if(thisxh>oxh) { return 1; } if(thisxh<oxh) { return -1; } return 0; } } class 班级 implements Comparable<班级>{ String 班号; ArrayList<学生> students; int 参与考试的人数=0; double sum=0; public ArrayList<选课> xuanke; public 班级(String 班号) { students=new ArrayList(); xuanke=new ArrayList(); this.班号 = 班号; } public void 添加学生(学生 stu) { students.add(stu); } public void 选课信息添加(选课 choice) { xuanke.add(choice); } public void 计算总成绩平均分(double 成绩) { sum=sum+成绩; 参与考试的人数=参与考试的人数+1; } @Override public int compareTo(班级 o) { // TODO Auto-generated method stub //String thisx=this.班号.substring(this.班号.length()-2); //String ox=o.班号.substring(o.班号.length()-2); int thisxh=Integer.parseInt(this.班号);//this学号 int oxh=Integer.parseInt(o.班号);//o学号 if(thisxh>oxh) { return 1; } if(thisxh<oxh) { return -1; } return 0; } } class 成绩{ public double 总成绩; public double 平时成绩=0; public double 期末成绩; public 成绩() { super(); } /*double 平时成绩; double 期末成绩; double 总成绩;*/ } class 考试成绩 extends 成绩{ int 平时成绩; int 期末成绩; public 考试成绩(int 平时成绩, int 期末成绩) { super(); this.平时成绩 = 平时成绩; this.期末成绩 = 期末成绩; super.平时成绩=平时成绩; super.期末成绩=期末成绩; 总成绩=(平时成绩*0.3+期末成绩*0.7); } } class 考察成绩 extends 成绩{ int 期末成绩; public 考察成绩(int 期末成绩) { super(); this.期末成绩 = 期末成绩; 总成绩=(期末成绩); } } class 选课{ 学生 学生a; String 课程a; public 成绩 成绩a; public 选课(学生 学生a, String 课程a, 成绩 成绩a) { super(); this.学生a = 学生a; this.课程a = 课程a; this.成绩a = 成绩a; } } class 输出{ ArrayList<课程>lesson;//课程信息 ArrayList <班级>classes;//班级信息 ArrayList <选课>choice;//课程学生成绩信息; public 输出(ArrayList<课程> lesson, ArrayList<班级> classes, ArrayList<选课> choice) { super(); this.lesson = lesson; this.classes = classes; this.choice = choice; } public void systemoutStudent() {//1)学生课程总成绩平均分按学号由低到高排序输出,格式:学号+英文空格+姓名+英文空格+总成绩平均分 for(int i=0;i<classes.size();i++) { if(classes.get(i)!=null) { Collections.sort(classes.get(i).students); for(int k=0;k<classes.get(i).students.size();k++) {//一个班级里所有学生k的循环 int flag=1;//判断该学生是否有成绩 double sum=0;//每个学生的总成绩 double count=0; for(int j=0;j<classes.get(i).xuanke.size();j++) {//一个班里期中一个学生k的所有选课成绩 if(classes.get(i).xuanke.get(j).学生a.学号.equals(classes.get(i).students.get(k).学号)) { flag=0; sum=sum+classes.get(i).xuanke.get(j).成绩a.总成绩; count=count+1; } } if(flag==1) {//如果某个学生没有任何成绩信息,输出:学号+英文空格+姓名+英文空格+"did not take any exams" System.out.println(classes.get(i).students.get(k).学号+" "+classes.get(i).students.get(k).姓名+" "+"did not take any exams"); } if(flag==0) { System.out.println(classes.get(i).students.get(k).学号+" "+classes.get(i).students.get(k).姓名+" "+(int)Math.floor(sum/count)); classes.get(i).计算总成绩平均分(Math.floor(sum/count)); } } } } } public void systemoutClasses() {//班级所有课程总成绩平均分按班级由低到高排序输出 for(int i=0;i<classes.size();i++) { double sum=0; int flagg=0; for(int j=0;j<classes.get(i).xuanke.size();j++) { flagg=1; } if(flagg==1) {//有sum班级名称+英文空格+ "has no grades yet"//班级号+英文空格+总成绩平均分 System.out.println(classes.get(i).班号+" "+(int)Math.floor(classes.get(i).sum/(double)classes.get(i).参与考试的人数)); } if(flagg==0) {//该班级没有分数 System.out.println(classes.get(i).班号+" has no grades yet"); } } } public void systemoutLesson() {//单门课程成绩平均分分为三个分值:平时成绩平均分(可选)、期末考试平均分、总成绩平均分,按课程名称的字符顺序输出 //格式:课程名称+英文空格+平时成绩平均分+英文空格+期末考试平均分+英文空格+总成绩平均分 //如果某门课程没有任何成绩信息,输出:课程名称+英文空格+"has no grades yet" for(int i=0;i<lesson.size();i++) {//遍历课程表 double 平时成绩=0; double 期末成绩=0; double 总成绩=0; int flag=2;//判断该课程是否有成绩,2是没有,1是考试,0是考察 double count=0;//考试人数 课程 kc=lesson.get(i);//kc是某节课 for(int j=0;j<choice.size();j++) {//遍历选课信息,找到kc这堂课对应的成绩 if(choice.get(j).课程a.equals(kc.课程名称)) { if(kc.考核方式.equals("考试")) {//找到之后,看对应的考核方式,考试的话就要累加平时成绩与期末成绩和总成绩 flag=1; 平时成绩=平时成绩+choice.get(j).成绩a.平时成绩; 期末成绩=期末成绩+choice.get(j).成绩a.期末成绩; 总成绩=总成绩+choice.get(j).成绩a.总成绩; count++; } if(kc.考核方式.equals("考察")) {//考察,choice.get(j)是找到的对应kc的选课信息 flag=0; 总成绩=总成绩+choice.get(j).成绩a.总成绩; count=count+1.0; } } } //格式:课程名称+英文空格+平时成绩平均分+英文空格+期末考试平均分+英文空格+总成绩平均分 if(flag==0) {//考察 System.out.println(kc.课程名称+" "+(int)Math.floor(总成绩/count)+" "+(int)Math.floor(总成绩/count)); } if(flag==1) {//考试 System.out.println(kc.课程名称+" "+(int)Math.floor(平时成绩/count)+" "+(int)Math.floor(期末成绩/count)+" "+(int)Math.floor(总成绩/count)); } if(flag==2) {//没分!输出:课程名称+英文空格+"has no grades yet" System.out.println(kc.课程名称+" "+"has no grades yet"); } } } }
2.课程成绩统计程序-2
1)类图
2)生成报表
(绿色环形区域即被测量维度的期望值,维度上的点则是测量得到的实际值)
据报表分析:
①平均圈复杂度和函数深度超标,圈复杂度大说明程序代码的判断逻辑复杂,代码质量较低(其实是因为我把判断输入是否正确的代码大部分都放在了主函数,所以主函数写的过于冗长)
②耦合度过高:类与类间分支嵌套过多。
3)代码解释:
课程成绩统计程序-2在第一次的基础上增加了实验课,如果上一次作业的类设计比较清晰的话,其实这次作业比较容易完成。
这次我在上次代码的基础上,新增了一个实验成绩类,同样是继承成绩类,可以看到代码如下:
class 实验成绩 extends 成绩{ ArrayList<Integer> 实验分数=new ArrayList<Integer>(); public 实验成绩(ArrayList<Integer> 实验分数) { super(); this.实验分数 = 实验分数; int sum=0; for(int i=0;i<实验分数.size();i++) { sum=实验分数.get(i)+sum; } 总成绩=sum/实验分数.size(); } }
这个实验类里面有一个ArrayList数组用来存放实验分数,这是因为实验成绩的分数的数量是不固定的,所以用ArrayList数组刚刚好。
除此之外,改动还有在main方法中新增了判断实验成绩,实验课的部分,输出类也增添了输出实验成绩的部分。
4)部分相比之前修改过的代码如下:
(main方法里新增的判断实验成绩的一栏)
else if(brr.length>5&&brr[0].matches("^[0-9]{8}$")&&brr[1].length()<=10&&brr[2].length()<=10&&isNum(brr[3])) { if(!(Integer.parseInt(brr[3])>=4&&Integer.parseInt(brr[3])<=9)) { System.out.println("wrong format"); continue; } int x=0; int yc=0; int flagg=0; String arr[]=l.split(" "); for(int i=0;i<arr.length;i++) { if(i>=4) { if(Integer.parseInt(arr[i])>100||Integer.parseInt(arr[i])<0) { System.out.println("wrong format"); flagg=1; break; } } } if(flagg==1) { continue; } String 班号=arr[0].substring(0,6); for(String s:成绩记录) {//添加过该条成绩 if(s.equals(arr[0]+arr[1]+arr[2])) { x=1; } } if(x==1) { continue; } 成绩记录.add(arr[0]+arr[1]+arr[2]); 学生 stu=new 学生(arr[1], arr[0]); int flag=0;//如果班号存过 for(int i=0;i<classes.size();i++) { if(班号.equals(classes.get(i).班号)) { flag=1;//班号存过 int temp=1;//1是没存过,0是存过 for(学生 b:classes.get(i).students) { if(b.学号.equals(stu.学号)) { temp=0; break; } } if(temp==1) { classes.get(i).添加学生(stu); } break; } }//flag=1; if(flag==0) {//班号没存过 班级 bj=new 班级(班号); bj.添加学生(stu); classes.add(bj); }//把学生添加到班级里 String typle=Typleoflesson(lesson, arr[2]);/// if(typle==null) {//学号+英文空格+姓名+英文空格+":"+课程名称+英文空格+"does not exist" //System.out.println(arr[0]+" "+arr[1]+" "+arr[2]+" "+"dose not exist"); System.out.println(arr[2]+" does not exist"); yc=1; } else if(typle.equals("实验")) { int num=Integer.parseInt(brr[3]); if((arr.length-4)!=num) { System.out.println(arr[0]+" "+arr[1]+" "+": access mode mismatch");// } else { ArrayList<Integer>xy=new ArrayList<Integer>(); for(int i=0;i<arr.length;i++) { if(i>=4) { xy.add(Integer.parseInt(arr[i])); } } 实验成绩 k=new 实验成绩(xy); 选课 choose=new 选课(stu, arr[2], k); for(int i=0;i<classes.size();i++) { if(班号.equals(classes.get(i).班号)) { classes.get(i).选课信息添加(choose); } } choice.add(choose); } } }
3.课程成绩统计程序-3
1)类图
2)生成报表
3)代码解释:
课程成绩统计程序-3在第二次的基础上修改了计算总成绩的方式。
考试课程成绩和实验课程成绩增加了一个“权重”部分,这个部分的修改我是通过修改实验成绩类,考试成绩类来实现:在实验成绩类中增加一个ArrayList数组,用来存放各个成绩的权重,考试成绩类的修改同上。
与此同时,我还在main方法中修改了相应输入格式的判断。
4)部分修改的代码如下:
class 实验课程 extends 课程{ ArrayList<Double>权重=new ArrayList(); public 实验课程(String 课程名称, String 课程性质, String 考核方式, ArrayList<Double> 权重) { super(课程名称, 课程性质, 考核方式); this.权重 = 权重; } } class 考试成绩 extends 成绩{ int 平时成绩=0; int 期末成绩=0; ArrayList<Double>权重=new ArrayList<Double>(); public 考试成绩(int 平时成绩, int 期末成绩, ArrayList<Double> 权重) { super(); this.平时成绩 = 平时成绩; this.期末成绩 = 期末成绩; this.权重 = 权重; 总成绩=(平时成绩*权重.get(0)+期末成绩*权重.get(1)); } }
三、踩坑心得
- 写正则表达式
由于正则表达式过于不熟练,导致第四次大作业好多测试点都没有过,第五次大作业出了后,我上网看了有关正则表达式的文章,学习了一下后运用到代码里,发现真的很方便。
这里给出我当时学习正则表达式用的文章:
[]:http://t.csdn.cn/ZmH67
- Comparable接口
期中考试考到了接口,Comparable的comepareTo方法搭配Collection.sort()方法可以实现对对象数组的排序,十分方便。
- 格式错误的踩坑
错误格式是因为漏了空格或多了回车,这需要不断调试代码并且对比题干才可以发现这类错误的原因。在拿到题目的时候。
- 审题与类设计
java是面向对象的语言,在题目给出后,应该先认真审题,将类都设计好,不然到后续发现漏了题目要求的时候,改起来很复杂。
同样,为了方便修改代码内容,应该降低类之间耦合性,熟悉类间关系进行设计。
四、主要困难与改进建议
①主要困难:
基础知识薄弱,对于很多新的语法不会用不敢用,如最近接触的哈希,Hashmap,Hashset。
②改进建议:
希望留大作业时不要只留一道菜单题,应该多留几道不算难但能锻炼学生灵活掌握新知识的题。
五、总结
经历了java第二个学习阶段,我深刻认识到在面向对象过程中对类的设计是多么重要,类与类间关系,接口和抽象类的设计等等,都是对我的考验。代码越好,代码的圈复杂度就越低,类与类之间关系明确,代码的可维护性也高。
再回归具体知识点,在本阶段学习中我训练了继承和接口的使用,也自主设计了面向对象的代码,并逐步接触了一些比较好用的类与方法,也对动态数组的使用逐渐熟悉。希望在学好目前的知识的基础上,能学到更深的知识。