第三次博客作业
第三次博客作业 22206107-郭子诚
一、前言:总结之前所涉及到的知识点、题量、难度等情况
1、知识点:
(1)面向对象进行属性和类的设计:
学生成绩程序需要定义学生类(Student),成绩类(Score),课程类(Course),班级类(Class),
其中学生类包含学生姓名、学号、总成绩等基本信息,
成绩类包含学生得分、科目名称等属性,
课程类包括学生的姓名,学生成绩等基本信息……
(2)set的用法:
set集合是一组唯一的元素,每个元素在集合中只能出现一次,所以无法传入相同的数据在set集合中。
在Java中,有HashSet和TreeSet两种不同的方法排序。
HashSet是一个无序的集合,以插入的顺序来决定元素的存储位置。
而TreeSet可以通过函数进行自定义排序。例子如下:
在我第六次作业 课程成绩统计程序-1 中课程的排序中:
Set<Course> courses = new TreeSet<>(new Comparator<Course>(){ @Override public int compare(Course o1, Course o2) { try { Comparator compator = Collator.getInstance(Locale.CHINA); if (compator.compare(o1.coursename, o2.coursename) < 0) { return -1; } else if (compator.compare(o1.coursename, o2.coursename) > 0) { return 1; } } catch (Exception e) { } return 0; // Comparator compator = Collator.getInstance(Locale.CHINA); // o1.number.compareTo(o2.number) } });
(3)list的用法:
list集合包含一个固定顺序的元素列表,每个元素可以通过其在列表中的索引位置进行访问。
这意味着list中的对象是有序的,可以包含重复的元素,这点和set刚好相反。在Java中,我一般喜欢用ArrayList进行list实现。
举例:
在我第六次作业 课程成绩统计程序-1 中学生成绩动态数组的建立时:
List<Integer> studentgrade = new ArrayList<>();
(4)map的用法:
Map是Java中的一个集合类,用于存储键值对。Map中的每个元素都是一个键值对,其中键是唯一的,每个键只能对应一个值。Map中常用的实现类有HashMap、TreeMap等。
举例:
Map<String, Integer> scores = new HashMap<String, Integer>(); // 向Map中添加元素 scores.put("Alice", 95); scores.put("Bob", 80); 经过这一步之后,相当于Alice=95,Bob=80, 遍历Map中的键值对: for (Map.Entry<String, Integer> entry : scores.entrySet()) { String name = entry.getKey(); Integer score = entry.getValue(); System.out.println(name + "'s score: " + score); }
(5)continue的用法
Continue平时大多数人用的不多,但是在这里的循环用处很大。可以直接开启下一个循环。
一般来说,当某个条件不满足时,循环语句会终止,但使用continue后,循环语句不会终止,直接进行下一次迭代。
2、题量:
这几周的题量适中,代码量在350行-500行之间。
3、难度:
难度相对于点菜系统有所降低,主要是因为思路相比于点菜系统更加清晰,学生,成绩,课程,班级的关系很容易就可以体现出来。
二、设计与分析:(重点对题目的提交源码进行分析,可参考SourceMonitor的生成报表内容以及PowerDesigner的相应类图,要有相应的解释和心得(做到有图有真相),本次Blog必须分析PTA中成绩计算系列的题目。)
1、主题干:
某高校课程从性质上分为:必修课、选修课,从考核方式上分为:考试、考察。
考试的总成绩由平时成绩、期末成绩分别乘以权重值得出,比如平时成绩权重0.3,期末成绩权重0.7,总成绩=平时成绩*0.3+期末成绩*0.7。
考察的总成绩直接等于期末成绩
必修课的考核方式必须为考试,选修课可以选择考试、考察任一考核方式。
分析:
在我的代码中,并未用到所谓的继承和多态方法,依旧优先选择聚合,组合和关联的关系。
因为通过分析可以知道,班级里面包括了学生这个类,而课程中也包含了学生,而一个学生又要选择不同的课程,所以是相互聚合的关系。我用了成绩这个属性将两者联系起来,在保证数据清晰的同时,又保证了低耦合的特性。
2、输入:
包括课程、课程成绩两类信息。
课程信息包括:课程名称、课程性质、考核方式(可选,如果性质是必修课,考核方式可以没有)三个数据项。
课程信息格式:课程名称+英文空格+课程性质+英文空格+考核方式
课程性质输入项:必修、选修
考核方式输入选项:考试、考察
课程成绩信息包括:学号、姓名、课程名称、平时成绩(可选)、期末成绩
课程信息格式:学号+英文空格+姓名+英文空格+课程名称+英文空格+平时成绩+英文空格+期末成绩
分析:
首先在输入中,每一行的输入必然少不了英文空格,所以可以借英文空格把整体拆分成不同的部分。拆分了之后根据每部分part的数量推理出它的考核方式。
在parts.length==3时说明在录入课程信息;
在parts.length==4时说明在录入考察课学生信息;
在parts.length==5时说明在录入考试课学生信息(因为有平时和期末两部分成绩)。
也可以根据parts.length的值判断是否有多输入或少输入的情况。
同时也对信息做出了一定的约束,例如平时成绩和期末成绩的权重默认为0.3、0.7,成绩是整数,不包含小数部分,成绩的取值范围是【0,100】,学号由8位数字组成,姓名不超过10个字符,课程名称不超过10个字符等等。这些情况在输出wrong format的同时,也要通过continue开启下一个循环。
3、输出:
输出包含三个部分,包括学生所有课程总成绩的平均分、单门课程成绩平均分、单门课程总成绩平均分、班级所有课程总成绩平均分。
为避免误差,平均分的计算方法为累加所有符合条件的单个成绩,最后除以总数。
1)学生课程总成绩平均分按学号由低到高排序输出
格式:学号+英文空格+姓名+英文空格+总成绩平均分
如果某个学生没有任何成绩信息,输出:学号+英文空格+姓名+英文空格+"did not take any exams"
2)单门课程成绩平均分分为三个分值:平时成绩平均分(可选)、期末考试平均分、总成绩平均分,按课程名称的字符顺序输出
格式:课程名称+英文空格+平时成绩平均分+英文空格+期末考试平均分+英文空格+总成绩平均分
如果某门课程没有任何成绩信息,输出:课程名称+英文空格+"has no grades yet"
3)班级所有课程总成绩平均分按班级由低到高排序输出
格式:班级号+英文空格+总成绩平均分
如果某个班级没有任何成绩信息,输出:班级名称+英文空格+ "has no grades yet"
分析:
学号由低到高排序解决方案:
Set<Student> students = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { int n1=o1.number.length()-o2.number.length(); int n2=o1.number.compareTo(o2.number); return n1==0?n2:n1; } });
这里返回0则表示长度一样,则进行n2比较,否则进行n1字符串长度比较。
如果学生没有任何成绩信息,说明学生类里面没有课程数据,在这种情况下可以将把学生成绩定位-1,在最后循环的时候进行输出。
平均分有三个分值,分别是平时成绩平均分、期末考试平均分、总成绩平均分。
但在考察课程中,前两者都不存在,所以可以直接把平时成绩平均分和期末考试平均分都定为0,在输出的时候也进而受到限制。
如果某门课程没有任何成绩信息,说明课程类里面没有学生数据,情况和上面的类似。
班级没有任何成绩信息情况也一样。
4、异常情况:
1)如果解析某个成绩信息时,课程名称不在已输入的课程列表中,输出:学号+英文空格+姓名+英文空格+":"+课程名称+英文空格+"does not exist"
2)如果解析某个成绩信息时,输入的成绩数量和课程的考核方式不匹配,输出:学号+英文空格+姓名+英文空格+": access mode mismatch"
以上两种情况如果同时出现,按第一种情况输出结果。
3)如果解析某个课程信息时,输入的课程性质和课程的考核方式不匹配,输出:课程名称+" : course type & access mode mismatch"
4)格式错误以及其他信息异常如成绩超出范围等,均按格式错误处理,输出"wrong format"
5)若出现重复的课程/成绩信息,只保留第一个课程信息,忽略后面输入的。
分析:
如果课程名称不在已输入的课程列表中,可以用一个标志符sign来表示是否找到了该课程名称,输出的时候作为其中的一个判断条件。
如果输入的成绩数量和课程的考核方式不匹配,可以根据parts.length的值判断是否有多输入或少输入的情况。
当输入的课程性质和课程的考核方式不匹配,只有一种情况:即必修和考察不能同时出现。
如果想保留第一个课程信息,可以通过set动态数组储存数据,也可以循环一轮把多的部分剔除掉。
5、信息约束:
成绩平均分只取整数部分,小数部分丢弃
三、采坑心得:(对源码的提交过程中出现的问题及心得进行总结,务必做到详实,拿数据、源码及测试结果说话,切忌假大空)
1. 数字精度问题:
在一开始没有注意到数字精度对数据结果的影响,在后面将double改成float,把1改成0.99-1.01之间,测试点就莫名奇妙地通过了!
(可以看出来,一开始只有样例通过了测试点,纠结了好久)

我写的代码:
if(zong!=1) { System.out.printf("%s : weight value error\n",coursename); continue; }
在后期坤王的修改下:
if(zong<0.999||zong>1.0001) { System.out.printf("%s : weight value error\n",coursename); continue; }
本身计算的时候就会有精度损失,所以在逼近1的区间内必须这样做。
四、改进建议:(对相应题目的编码改进给出自己的见解,做到可持续改进)
1.数据结构的选择:
在设计学生成绩程序时需要明确使用何种数据结构来存储学生信息和成绩信息。
如果只需要记录学生的基本信息和成绩,可以使用类似动态数组或列表这样的简单数据结构进行存储,
但对于更复杂的需求,可以选择使用Map的方式进行数据的整理。
Map是Java中的一个集合类,用于存储键值对。Map中的每个元素都是一个键值对,其中键是唯一的,每个键只能对应一个值。
Map中常用的实现类有HashMap、TreeMap等。下面是Map的用法举例:
import java.util.HashMap; import java.util.Map; public class MapExample { public static void main(String[] args) { // 创建一个HashMap实例 Map<String, Integer> scores = new HashMap<String, Integer>(); // 向Map中添加元素 scores.put("Alice", 95); scores.put("Bob", 80); scores.put("Cathy", 90); scores.put("David", 88); // 判断Map是否为空 boolean isEmpty = scores.isEmpty(); System.out.println("Map is empty: " + isEmpty); // 获取Map的大小 int size = scores.size(); System.out.println("Map size: " + size); // 使用键来获取值 Integer aliceScore = scores.get("Alice"); System.out.println("Alice's score: " + aliceScore); // 遍历Map中的键值对 for (Map.Entry<String, Integer> entry : scores.entrySet()) { String name = entry.getKey(); Integer score = entry.getValue(); System.out.println(name + "'s score: " + score); } // 删除Map中的元素 scores.remove("David"); size = scores.size(); System.out.println("After removing David, the map size: " + size); } }
2.实现高内聚低耦合
可以修改类结构,将成绩类的继承关系改为组合关系,成绩信息由课程成绩类和分项成绩类组成,课程成绩类组合分项成绩类,分项成绩类由成绩分值和权重两个属性构成。
五、总结:(对本阶段(10-16周)综合性总结,学到了什么,哪些地方需要进一步学习及研究,对教师、课程、作业、实验、课上及课下组织方式等方面的改进建议及意见。)
在学生成绩程序的实现中,需要考虑到存储数据的结构设计,如何组织学生信息和成绩信息,可以选择使用Java中的常用集合类如List、Map等,或者使用数据库进行存储。在读取和写入文件时,要注意保证读写操作的准确性和效率。在查询、排序和统计等功能的实现过程中,需要注意使用合适的算法和数据结构,以便提高程序的性能和准确性。在调试和测试阶段,需要保证程序的各项功能可以正常运行,对于各种异常情况和错误,需要进行充分的测试和处理。
希望老师上课的时候多教点语法,并根据课堂教的语法练习pta。
附录1:课程成绩统计程序-3代码
import java.util.*; import java.util.HashSet; import java.util.Set; import java.lang.Comparable; import java.text.Collator; import java.util.ArrayList; import java.util.List; class Course{ String coursename = new String(); String coursetype = new String(); String courseway = new String(); @SuppressWarnings("unchecked") Set<Grade> grades = new HashSet<>(); public Course(String coursename, String coursetype, String courseway) { this.coursename = coursename; this.coursetype =coursetype; this.courseway = courseway; } public void studentlist1(int dailygrade, int finalgrade,int totalgrade) { grades.add(new Grade(dailygrade,finalgrade,totalgrade)); } public int avetotalgrade() { int k=0; double zong2=0; for(Grade intstudent2: grades) { zong2+=intstudent2.totalgrade; k++; } return (int) (zong2/k); } } class Choosecourse{ String coursename = new String(); @SuppressWarnings("unchecked") List<Float> jiaquan = new ArrayList<>(); public Choosecourse(String coursename, List<Float> dest) { this.coursename = coursename; for(int i=0;i<dest.size();i++){ jiaquan.add(dest.get(i)); //将源动态数组中的元素逐一添加到目的动态数组中 } } } class Bicourse{ String coursename = new String(); float dailygrade; float finalgrade; public Bicourse(String coursename,float dailygrade, float finalgrade) { this.coursename = coursename; this.dailygrade = dailygrade; this.finalgrade = finalgrade; } } class Student{ // 学号+英文空格+姓名+英文空格+课程名称+英文空格+平时成绩+英文空格+期末成绩 String number = new String(); String name = new String(); public Student( String number ,String name) { this.number = number; this.name = name; } @SuppressWarnings("unchecked") List<Integer> studentgrade = new ArrayList<>(); public void studentlist(int classnumber) { studentgrade.add(classnumber); } public int avetotalgrade(){ int j=0; float zong1=0; for(int intstudentgrade: studentgrade) { zong1+=intstudentgrade; j++; } return (int) (zong1/j); } } class Class{ String classnumber = new String(); public Class(String classnumber) { this.classnumber = classnumber; } @SuppressWarnings("unchecked") List<Integer> studentgrade1 = new ArrayList<>(); public void classlist(int classnumber) { studentgrade1.add(classnumber); } public int avetotalgrade(){ int j=0; float zong1=0; for(int intstudentgrade: studentgrade1) { zong1+=intstudentgrade; j++; } return (int) (zong1/j); } } class Grade{ int dailygrade; int finalgrade; int totalgrade; public Grade(int dailygrade, int finalgrade,int totalgrade) { this.dailygrade = dailygrade; this.finalgrade = finalgrade; this.totalgrade = totalgrade; } } public class Main { public static void main(String[] args) { // TODO Auto-generated method stub Scanner scanner = new Scanner(System.in); @SuppressWarnings("unchecked") Set<Course> courses = new TreeSet<>(new Comparator<Course>(){ @Override public int compare(Course o1, Course o2) { try { Comparator compator = Collator.getInstance(Locale.CHINA); if (compator.compare(o1.coursename, o2.coursename) < 0) { return -1; } else if (compator.compare(o1.coursename, o2.coursename) > 0) { return 1; } } catch (Exception e) { } return 0; // Comparator compator = Collator.getInstance(Locale.CHINA); // o1.number.compareTo(o2.number) } }); Set<Choosecourse> choosecourses = new HashSet<>(); Set<Bicourse> bicourses = new HashSet<>(); //所有课程,键为课程名,按课程名排序 Set<Student> students = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { int n1=o1.number.length()-o2.number.length(); int n2=o1.number.compareTo(o2.number); return n1==0?n2:n1; //返回0则表示长度一样,则进行n2比较,否则进行n1字符串长度比较 } }); @SuppressWarnings("unchecked") Set<Class> classes = new TreeSet<>(new Comparator<Class>() { @Override public int compare(Class o1,Class o2) { int n1=o1.classnumber.length()-o2.classnumber.length(); int n2=o1.classnumber.compareTo(o2.classnumber); return n1==0?n2:n1; //返回0则表示长度一样,则进行n2比较,否则进行n1字符串长度比较 } }); while (scanner.hasNext()) { String line = scanner.nextLine(); if (line.equals("end")) { break; } else { String[] parts = line.split("\\s+"); if(parts[1].equals("实验")&&parts[2].equals("实验")){ String coursename = parts[0]; String coursetype = parts[1]; String courseway = parts[2]; if(coursename.length()>10){ System.out.printf("wrong format\n"); continue; } @SuppressWarnings("unchecked") List<Float> dest = new ArrayList<>(); int n = Integer.parseInt(parts[3]); float zong=0; if(n<4||n>9||coursename.length()>10){ System.out.printf("wrong format\n"); continue; } if((parts.length-4)==n){ for(int i=4;i<parts.length;i++) { float num = Float.parseFloat(parts[i]); zong += num; dest.add(num); } } else { System.out.printf("%s : number of scores does not match\n",coursename); continue; } if(zong<0.999||zong>1.0001) { System.out.printf("%s : weight value error\n",coursename); continue; } choosecourses.add(new Choosecourse(coursename,dest)); courses.add(new Course(coursename,coursetype,courseway)); } else if(parts[2].equals("考试")){ String coursename = parts[0]; String coursetype = parts[1]; String courseway = parts[2]; float dailygrade = Float.parseFloat(parts[3]); float finalgrade= Float.parseFloat(parts[4]); if(coursename.length()>10){ System.out.printf("wrong format\n"); continue; } if(!(coursetype.equals("必修")||coursetype.equals("选修"))) { System.out.printf("wrong format\n"); continue; } bicourses.add(new Bicourse(coursename,dailygrade,finalgrade)); courses.add(new Course(coursename,coursetype,courseway)); } else if(parts.length==3){ String coursename = parts[0]; String coursetype = parts[1]; String courseway = parts[2]; if(coursename.length()>10){ System.out.printf("wrong format\n"); continue; } else if(!(coursetype.equals("必修")||coursetype.equals("选修")||coursetype.equals("实验"))) { System.out.printf("wrong format\n"); continue; } else if(!(courseway.equals("考察")||courseway.equals("考试")||courseway.equals("实验"))) { System.out.printf("wrong format\n"); continue; } else if(courseway.equals("考察")&&coursetype.equals("选修")) { courses.add(new Course(coursename,coursetype,courseway)); } else { System.out.printf("%s : course type & access mode mismatch\n",coursename); continue; } } else if(parts.length==4) { String number = parts[0]; String name = parts[1]; String coursename = parts[2]; if(!parts[3].matches("^(100|[0-9]{1,2})$")) { System.out.printf("wrong format\n"); continue; } int totalgrade = Integer.parseInt(parts[3]); int sign=0; int sign1=0; if(totalgrade>100||totalgrade<0||number.length()!=8||name.length()>10||coursename.length()>10){ System.out.printf("wrong format\n"); continue; } classes.add(new Class(number.substring(0, 6))); students.add(new Student(number ,name)); for(Course intcourse : courses) { if(intcourse.coursename.equals(coursename)&&intcourse.courseway.equals("考察")) { intcourse.studentlist1(0, 0, totalgrade); for(Class intclass : classes) { if(intclass.classnumber.equals(number.substring(0, 6))) {intclass.classlist(totalgrade); break; } } for(Student intstudent : students){ if(intstudent.number.equals(number)){ intstudent.studentlist(totalgrade); break;} } } else if(intcourse.coursename.equals(coursename)&&(intcourse.courseway.equals("考试")||intcourse.courseway.equals("实验"))){ sign1=1; break; } } for(Course intcourse : courses) { if(intcourse.coursename.equals(coursename)){ sign=1; break; } } if(sign==0){ // students.add(new Student(number ,name ,coursename,0 , 0 ,0)); System.out.printf("%s does not exist\n",coursename); } else if(sign1==1){ System.out.printf("%s %s : access mode mismatch\n",number, name); } } else if(parts.length==5){ int sign=0; int sign1=0; String number = parts[0]; String name = parts[1]; String coursename = parts[2]; if(!parts[3].matches("^(100|[0-9]{1,2})$") || !parts[4].matches("^(100|(([1-9]){1}[0-9]?|0{1})((\\.)([0-9]){1,2})?)$")) { System.out.printf("wrong format\n"); continue; } int dailygrade = Integer.parseInt(parts[3]); int finalgrade= Integer.parseInt(parts[4]); int totalgrade = 0; for(Course intcourse : courses) { float totalgrade1 = 0; if(intcourse.coursename.equals(coursename)&&intcourse.courseway.equals("考试")){ for(Bicourse intbicourse : bicourses) { if(intbicourse.coursename.equals(coursename)) { totalgrade1 = dailygrade * intbicourse.dailygrade + finalgrade * intbicourse.finalgrade; break; } else sign1=1; } } totalgrade= (int)(totalgrade1); } if(totalgrade>100||totalgrade<0||dailygrade>100||dailygrade<0||finalgrade>100||finalgrade<0||number.length()!=8||name.length()>10||coursename.length()>10){ System.out.printf("wrong format\n"); continue; } students.add(new Student(number ,name)); classes.add(new Class(number.substring(0, 6))); for(Course intcourse : courses) { if(intcourse.coursename.equals(coursename)&&intcourse.courseway.equals("考试")) { intcourse.studentlist1(dailygrade, finalgrade, totalgrade); for(Class intclass : classes) { if(intclass.classnumber.equals(number.substring(0, 6))) {intclass.classlist(totalgrade); break; } } for(Student intstudent : students){ if(intstudent.number.equals(number)) {intstudent.studentlist(totalgrade); break;} } } else if(intcourse.coursename.equals(coursename)&&(intcourse.courseway.equals("考察")||intcourse.courseway.equals("实验"))){ sign1=1; break; } } for(Course intcourse : courses) { if(intcourse.coursename.equals(coursename)){ sign=1; break; } } if(sign==0){ System.out.printf("%s does not exist\n",coursename); } else if(sign1==1){ System.out.printf("%s %s : access mode mismatch\n",number, name); } } else if(parts.length>5){ int sign=0; int sign1=0; int sign2=0; String number = parts[0]; String name = parts[1]; String coursename = parts[2]; int totalgrade=0; float totalgrade1 = 0; for(Course intcourse : courses) { if(intcourse.coursename.equals(coursename)&&intcourse.courseway.equals("实验")){ for(Choosecourse intchoosecourse : choosecourses){ if(intchoosecourse.coursename.equals(coursename)) { if(intchoosecourse.jiaquan.size()!= parts.length-3) { sign1=1; break; } for(int i=3;i<parts.length;i++) { totalgrade1 += Integer.parseInt(parts[i])*intchoosecourse.jiaquan.get(i-3); } break; } } } } totalgrade= (int) (totalgrade1); if(number.length()!=8||name.length()>10||coursename.length()>10||sign2==1){ System.out.printf("wrong format\n"); continue; } classes.add(new Class(number.substring(0, 6))); students.add(new Student(number ,name)); for(Course intcourse : courses) { if(intcourse.coursename.equals(coursename)&&intcourse.courseway.equals("实验")&&sign1==0) { intcourse.studentlist1(0, 0, totalgrade); for(Class intclass : classes) { if(intclass.classnumber.equals(number.substring(0, 6))&&sign1==0) {intclass.classlist(totalgrade); break; } } for(Student intstudent : students){ if(intstudent.number.equals(number)){ intstudent.studentlist(totalgrade); break;} } } else if(intcourse.coursename.equals(coursename)&&(intcourse.courseway.equals("考试")||intcourse.courseway.equals("考察"))){ sign1=1; break; } } for(Course intcourse : courses) { if(intcourse.coursename.equals(coursename)){ sign=1; break; } } if(sign==0){ // students.add(new Student(number ,name ,coursename,0 , 0 ,0)); System.out.printf("%s does not exist\n",coursename); } else if(sign1==1){ System.out.printf("%s %s : access mode mismatch\n",number, name); } } else System.out.printf("wrong format\n"); } } for(Student student : students){ if(student.avetotalgrade()==0){ System.out.printf("%s %s did not take any exams\n",student.number, student.name); } else System.out.printf("%s %s %d\n",student.number, student.name ,student.avetotalgrade()); } for(Course course : courses) { if(course.avetotalgrade() ==0 ){ System.out.printf("%s has no grades yet\n",course.coursename); } else { System.out.printf("%s %d\n",course.coursename, course.avetotalgrade() ); } } for(Class classs : classes) { if(classs.avetotalgrade()==0) System.out.printf("%s has no grades yet\n",classs.classnumber); else System.out.printf("%s %d\n",classs.classnumber,classs.avetotalgrade()); } } }
附录2:类图

附录3:最后一次运行结果:


浙公网安备 33010602011771号