blog-3

发布时间 2023-12-10 00:00:06作者: motoral

一、前言:

在前面的学习中,我涉及了数据结构与算法、错误处理和异常情况、以及代码可维护性和可扩展性等多个知识点。通过实验和项目,我对Java语言的应用有了更深入的了解。在题量和难度方面,实验和项目的设置使我能够逐步深入学习,并应用所学知识解决实际问题,提高了我的编程能力。

在数据结构与算法方面,我学习了使用ArrayList和HashMap等数据结构,以及设计灵活的算法解决实际问题。在错误处理和异常情况方面,我深入理解了Java的异常处理机制,并实践了输入数据验证、异常处理和日志记录等策略。在代码可维护性和可扩展性方面,我注重了良好的代码结构、注释和模块化设计,运用面向对象的思想和设计模式,提高了代码的可读性和可维护性。

总体来说,前期的学习使我建立了坚实的Java编程基础,为后续的学习和项目实践打下了良好的基础。接下来,我将继续深入学习更高级的Java特性和开发技巧,以不断提升自己的编程水平。

对于第七次题目集成绩计算系列:

7-3 课程成绩统计程序-2
分数 60
作者 蔡轲
单位 南昌航空大学

课程成绩统计程序-2在第一次的基础上增加了实验课,以下加粗字体显示为本次新增的内容。

某高校课程从性质上分为:必修课、选修课、实验课,从考核方式上分为:考试、考察、实验。

考试的总成绩由平时成绩、期末成绩分别乘以权重值得出,比如平时成绩权重0.3,期末成绩权重0.7,总成绩=平时成绩*0.3+期末成绩*0.7。

考察的总成绩直接等于期末成绩

实验的总成绩等于课程每次实验成绩的平均分

必修课的考核方式必须为考试,选修课可以选择考试、考察任一考核方式。实验课的成绩必须为实验。

1、输入:

包括课程、课程成绩两类信息。

课程信息包括:课程名称、课程性质、考核方式(可选,如果性质是必修课,考核方式可以没有)三个数据项。

课程信息格式:课程名称+英文空格+课程性质+英文空格+考核方式

课程性质输入项:必修、选修、实验

考核方式输入选项:考试、考察、实验

考试/考查课程成绩信息包括:学号、姓名、课程名称、平时成绩(可选)、期末成绩

考试/考查课程信息格式:学号+英文空格+姓名+英文空格+课程名称+英文空格+平时成绩+英文空格+期末成绩

实验课程成绩信息包括:学号、姓名、课程名称、实验次数、每次成绩

实验次数至少4次,不超过9次

实验课程信息格式:学号+英文空格+姓名+英文空格+课程名称+英文空格+实验次数+英文空格+第一次实验成绩+...+英文空格+最后一次实验成绩

以上信息的相关约束:

1)平时成绩和期末成绩的权重默认为0.3、0.7

2)成绩是整数,不包含小数部分,成绩的取值范围是【0,100】

3)学号由8位数字组成

4)姓名不超过10个字符

5)课程名称不超过10个字符

6)不特别输入班级信息,班级号是学号的前6位。

2、输出:

输出包含三个部分,包括学生所有课程总成绩的平均分、单门课程成绩平均分、单门课程总成绩平均分、班级所有课程总成绩平均分。

为避免误差,平均分的计算方法为累加所有符合条件的单个成绩,最后除以总数。

1)学生课程总成绩平均分按学号由低到高排序输出

格式:学号+英文空格+姓名+英文空格+总成绩平均分

如果某个学生没有任何成绩信息,输出:学号+英文空格+姓名+英文空格+"did not take any exams"

2)单门课程成绩平均分分为三个分值:平时成绩平均分(可选)、期末考试平均分、总成绩平均分,按课程名称的字符顺序输出

考试/考察课程成绩格式:课程名称+英文空格+平时成绩平均分+英文空格+期末考试平均分+英文空格+总成绩平均分

实验课成绩格式:课程名称+英文空格+总成绩平均分

如果某门课程没有任何成绩信息,输出:课程名称+英文空格+"has no grades yet"

3)班级所有课程总成绩平均分按班级由低到高排序输出

格式:班级号+英文空格+总成绩平均分

如果某个班级没有任何成绩信息,输出:班级名称+英文空格+ "has no grades yet"

异常情况:

1)如果解析某个成绩信息时,课程名称不在已输入的课程列表中,输出:学号+英文空格+姓名+英文空格+":"+课程名称+英文空格+"does not exist"

2)如果解析某个成绩信息时,输入的成绩数量和课程的考核方式不匹配,输出:学号+英文空格+姓名+英文空格+": access mode mismatch"

以上两种情况如果同时出现,按第一种情况输出结果。

3)如果解析某个课程信息时,输入的课程性质和课程的考核方式不匹配,输出:课程名称+" : course type & access mode mismatch"

4)格式错误以及其他信息异常如成绩超出范围等,均按格式错误处理,输出"wrong format"

5)若出现重复的课程/成绩信息,只保留第一个课程信息,忽略后面输入的。

信息约束:

1)成绩平均分只取整数部分,小数部分丢弃

参考类图(与第一次相同,其余内容自行补充):


e724fa4193aa9ee32e78a68cd96fd6df_22401e04-c501-4b28-bb65-dabe39d374e7.png

 

输入样例1:

在这里给出一组输入。例如:

java 实验 实验
20201103 张三 java 4 70 80 90
end

输出样例1:

在这里给出相应的输出。例如:

20201103 张三 : access mode mismatch
20201103 张三 did not take any exams
java has no grades yet
202011 has no grades yet

输入样例2:

在这里给出一组输入。例如:

java 实验 实验
20201103 张三 java 3 70 80 90
end

输出样例2:

在这里给出相应的输出。例如:

wrong format
java has no grades yet

输入样例3:

在这里给出一组输入。例如:

java 必修 实验
20201103 张三 java 3 70 80 90 100
end

输出样例3:

在这里给出相应的输出。例如:

java : course type & access mode mismatch
wrong format

输入样例4:

在这里给出一组输入。例如:

java 必修 实验
20201103 张三 java 4 70 80 90 105
end

输出样例4:

在这里给出相应的输出。例如:

java : course type & access mode mismatch
wrong format

 

输入样例5:

在这里给出一组输入。例如:

java 选修 考察
C语言 选修 考察
java实验 实验 实验
编译原理 必修 考试
20201101 王五 C语言 76
20201216 李四 C语言 78
20201307 张少军 编译原理 82 84
20201103 张三 java实验 4 70 80 90 100
20201118 郑觉先 java 80
20201328 刘和宇 java 77
20201220 朱重九 java实验 4 60 60 80 80
20201132 王萍 C语言 40
20201302 李梦涵 C语言 68
20201325 崔瑾 编译原理 80 84
20201213 黄红 java 82
20201209 赵仙芝 java 76
end

输出样例5:

在这里给出相应的输出。例如:

20201101 王五 76
20201103 张三 85
20201118 郑觉先 80
20201132 王萍 40
20201209 赵仙芝 76
20201213 黄红 82
20201216 李四 78
20201220 朱重九 70
20201302 李梦涵 68
20201307 张少军 83
20201325 崔瑾 82
20201328 刘和宇 77
C语言 65 65
java 78 78
java实验 77
编译原理 81 84 82
202011 70
202012 76
202013 77

 


我的答案:
import java.util.*;

class Course {
	
	int num=0;
     String name;
     String nature;
     String assessment;
}
class Student {
    String id;
    String cid;
    String name;
    Course course = new Course();
    ArrayList<Integer> grade=new ArrayList<>();
    int allgrade;
    int num;
    int getgrade() {
    	double grades=0;
    	if(grade.size()==1) {
    		grades=grade.get(0);
    	}
    	else { 
    		if(grade.size()==2) {
    		
    		grades=Math.floor(grade.get(0)* 0.3 +grade.get(1) * 0.7);
    		}
    		else {
    			double a=0;
    			for(int i=0;i<this.num;i++) {
    				a +=grade.get(i);
    			}
    			grades=Math.floor(a/num);
    		}
    	}
    	return (int)grades;
    }
}


public class Main{

	public static void main(String[] args) {
		
		
    HashMap<String, String> studentMap = new HashMap<>();
	ArrayList<Course> listcourse = new ArrayList<>();
	ArrayList<Student> liststudent = new ArrayList<>();
	HashMap<String, List<Integer>> scMap = new HashMap<>();
	HashMap<String, List<Integer>> Mapclass = new HashMap<>();
	HashMap<String, List<Integer>> Mapc = new HashMap<>();
	  Scanner scanner = new Scanner(System.in);
	String linedata =scanner.nextLine();
	while(!linedata.equals("end")) {
	String[] arr = linedata.split(" ");
	int size = arr.length;

	switch(size) {
	case 3:
		Course course = new Course();
		course.name=arr[0];
		course.nature=arr[1];
		course.assessment=arr[2];
		listcourse.add(course);
		
		break;
	case 4://考察
		
		Student student = new Student();
		student.id=arr[0];
		String clas=arr[0].substring(0, 6);
		student.cid=clas;
		student.name= arr[1];
		student.course.name=arr[2];
		for(Course course1:listcourse) {
			if(course1.name.equals(arr[2])) {
				course1.num++;
			}
		}
		student.grade.add(Integer.parseInt(arr[3]));
		liststudent.add(student);
		studentMap.put(arr[0], arr[1]+" "+student.getgrade());
		 if(Mapclass.containsKey(clas)) {
				Mapclass.get(clas).set(0,Mapclass.get(clas).get(0)+student.getgrade());
			
 	 }
 	 else {
 		 Mapclass.put(clas,new ArrayList<>());
      	Mapclass.get(clas).add(0,student.getgrade());
      	
 	 }

		 if(Mapc.containsKey(clas)) {
				Mapc.get(clas).set(0,Mapc.get(clas).get(0)+1);
		 }
		 else {
			 Mapc.put(clas,new ArrayList<>());
		       	Mapc.get(clas).add(1);
		 }
    		// for (Map.Entry<String, List<Integer>> entry : scMap.entrySet()) { 
    	    	
    	    	 if(scMap.containsKey(arr[2])) {
    	    			scMap.get(arr[2]).set(0,scMap.get(arr[2]).get(0)+Integer.parseInt(arr[3]));
    	    			scMap.get(arr[2]).set(1,scMap.get(arr[2]).get(1)+Integer.parseInt(arr[3]));

    	    	 }
    	    	 else {
    	    		 scMap.put(arr[2],new ArrayList<>());
    	         	scMap.get(arr[2]).add(0,Integer.parseInt(arr[3]));
    	     		scMap.get(arr[2]).add(1,Integer.parseInt(arr[3]));
    	    	 }
    		 

	
		break;
	
	case 5://考试
		
		Student student1 = new Student();
		student1.id=arr[0];
		
		student1.name= arr[1];
		student1.course.name=arr[2];
		for(Course course1:listcourse) {
			if(course1.name.equals(arr[2])) {
				course1.num++;
			}
		}
		student1.grade.add(Integer.parseInt(arr[3]));
		student1.grade.add(Integer.parseInt(arr[4]));
		liststudent.add(student1);
		studentMap.put(arr[0], arr[1]+" "+student1.getgrade());
		String clas1=arr[0].substring(0, 6);
		student1.cid=clas1;
		 if(Mapclass.containsKey(clas1)) {
				Mapclass.get(clas1).set(0,Mapclass.get(clas1).get(0)+student1.getgrade());
	 }
	 else {
		 Mapclass.put(clas1,new ArrayList<>());
		 Mapclass.get(clas1).add(0,student1.getgrade());
		
	 }
		 if(Mapc.containsKey(clas1)) {
				Mapc.get(clas1).set(0,Mapc.get(clas1).get(0)+1);
		 }
		 else {
			 Mapc.put(clas1,new ArrayList<>());
		       	Mapc.get(clas1).add(1);
		 }
	    	 if(scMap.containsKey(arr[2])) {
	    			scMap.get(arr[2]).set(0,scMap.get(arr[2]).get(0)+Integer.parseInt(arr[3]));
	    			scMap.get(arr[2]).set(1,scMap.get(arr[2]).get(1)+Integer.parseInt(arr[4]));
	    			scMap.get(arr[2]).set(2,scMap.get(arr[2]).get(2)+student1.getgrade());

	    	 }
	    	 else {
	    		 scMap.put(arr[2],new ArrayList<>());
	    		 scMap.get(arr[2]).add(0,Integer.parseInt(arr[3]));
					scMap.get(arr[2]).add(1,Integer.parseInt(arr[4]));
					scMap.get(arr[2]).add(2,student1.getgrade());
	    	 }
		 
		break;
	default://实验
		
		//20201103 张三 java实验 4 70 80 90 100
		Student student2 = new Student();
		student2.id=arr[0];
		student2.name= arr[1];
		student2.course.name=arr[2];
		
		for(Course course1:listcourse) {
			if(course1.name.equals(arr[2])) {
				course1.num++;
			}
		}
		student2.num=Integer.parseInt(arr[3]);
		for(int i=4;i<size;i++) {
			student2.grade.add(Integer.parseInt(arr[i]));
		}
		liststudent.add(student2);
		
		studentMap.put(arr[0], arr[1]+" "+student2.getgrade());

		String clas2=arr[0].substring(0, 6);
		student2.cid=clas2;
		 if(Mapclass.containsKey(clas2)) {
				Mapclass.get(clas2).set(0,Mapclass.get(clas2).get(0)+student2.getgrade());
	 }
	 else {
		 Mapclass.put(clas2,new ArrayList<>());
		 Mapclass.get(clas2).add(0,student2.getgrade());
		
	 }
		 if(Mapc.containsKey(clas2)) {
				Mapc.get(clas2).set(0,Mapc.get(clas2).get(0)+1);
		 }
		 else {
			 Mapc.put(clas2,new ArrayList<>());
		       	Mapc.get(clas2).add(1);
		 }
		    	 if(scMap.containsKey(arr[2])) {
		    		 scMap.get(arr[2]).set(0,scMap.get(arr[2]).get(0)+student2.getgrade());

		    	 }
		    	 else {
		    		 scMap.put(arr[2],new ArrayList<>());
		    		 scMap.get(arr[2]).add(0,student2.getgrade());
		    	 }
		    	
			 
			break;
	
	}//switch
	linedata= scanner.nextLine();
	}//while
	
	 List<Map.Entry<String, String>> list = new ArrayList<>(studentMap.entrySet());
     Collections.sort(list, (o1, o2) -> o1.getKey().compareTo(o2.getKey()));
     for (Map.Entry<String, String> entry : list) {
         System.out.println(entry.getKey() + " " + entry.getValue());
     }
     
    /* for (Map.Entry<String, List<Integer>> entry : scMap.entrySet()) { 
    	 System.out.println(entry.getKey() + ": " + entry.getValue());
    	 }
    	 */
     Map<String, List<Integer>> sumMap1 = new HashMap<>();
     

     for (Map.Entry<String, List<Integer>> entry : scMap.entrySet()) { 
    	 String key = entry.getKey();
    	 List<Integer> value = entry.getValue(); 
    	 
    		 for(Course course:listcourse) {
    			 if(key.equals(course.name)) {
    				 for(int i=0;i<value.size();i++) {
    					 value.set(i, (int) Math.floor(value.get(i)/course.num));
    				 }
    			 }
    		 }
    	 
    		 sumMap1.put(key, value);
    	 }
     

     
     Map<String, List<Integer>> sumMap2 = new HashMap<>();
     for (Map.Entry<String, List<Integer>> entry : Mapclass.entrySet()) { 
    	 String key = entry.getKey();
    	 List<Integer> value = entry.getValue(); 
    	 for (Map.Entry<String, List<Integer>> entry1 : Mapc.entrySet()) { 
    		 if(entry.getKey().equals(entry1.getKey())) {
    			 value.set(0, (int) Math.floor(value.get(0)/entry1.getValue().get(0)));
    		 }
    	 }
    		 sumMap2.put(key, value);
    	 }
     List<Map.Entry<String, List<Integer>>> list1 = new ArrayList<>(sumMap1.entrySet());
     Collections.sort(list1, (o1, o2) -> o1.getKey().compareTo(o2.getKey()));
     for (Map.Entry<String,List<Integer>> entry : list1) {
    	 System.out.print(entry.getKey());
    	 for(int i=0;i<entry.getValue().size();i++) {
    		 System.out.print(" "+entry.getValue().get(i));
    	 }
    	 System.out.print("\n");
        
     }
     List<Map.Entry<String, List<Integer>>> list2 = new ArrayList<>(sumMap2.entrySet());
     Collections.sort(list2, (o1, o2) -> o1.getKey().compareTo(o2.getKey()));
     for (Map.Entry<String,List<Integer>> entry : list2) {
    	 System.out.print(entry.getKey());
    	 for(int i=0;i<entry.getValue().size();i++) {
    		 System.out.print(" "+entry.getValue().get(i));
    	 }
    	 System.out.print("\n");
        
     }

     
	scanner.close();
	}
}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	

  

 

 

7-2 课程成绩统计程序-3
分数 64
作者 蔡轲
单位 南昌航空大学

课程成绩统计程序-3在第二次的基础上修改了计算总成绩的方式,

要求:修改类结构,将成绩类的继承关系改为组合关系,成绩信息由课程成绩类和分项成绩类组成,课程成绩类组合分项成绩类,分项成绩类由成绩分值和权重两个属性构成。

完成课程成绩统计程序-2、3两次程序后,比较继承和组合关系的区别。思考一下哪一种关系运用上更灵活,更能够适应变更。

题目最后的参考类图未做修改,大家根据要求自行调整,以下内容加粗字体显示的内容为本次新增的内容。

某高校课程从性质上分为:必修课、选修课、实验课,从考核方式上分为:考试、考察、实验。

考试的总成绩由平时成绩、期末成绩分别乘以权重值得出,比如平时成绩权重0.3,期末成绩权重0.7,总成绩=平时成绩*0.3+期末成绩*0.7。

考察的总成绩直接等于期末成绩

实验的总成绩等于课程每次实验成绩乘以权重后累加而得。

课程权重值在录入课程信息时输入。(注意:所有分项成绩的权重之和应当等于1)

必修课的考核方式必须为考试,选修课可以选择考试、考察任一考核方式。实验课的成绩必须为实验。

1、输入:

包括课程、课程成绩两类信息。

课程信息包括:课程名称、课程性质、考核方式、分项成绩数量、每个分项成绩的权重。

考试课信息格式:课程名称+英文空格+课程性质+英文空格+考核方式+英文空格+平时成绩的权重+英文空格+期末成绩的权重

考察课信息格式:课程名称+英文空格+课程性质+英文空格+考核方式

实验课程信息格式:课程名称+英文空格+课程性质+英文空格+考核方式+英文空格+分项成绩数量n+英文空格+分项成绩1的权重+英文空格+。。。+英文空格+分项成绩n的权重

实验次数至少4次,不超过9次

课程性质输入项:必修、选修、实验

考核方式输入选项:考试、考察、实验

考试/考查课程成绩信息包括:学号、姓名、课程名称、平时成绩(可选)、期末成绩

考试/考查课程成绩信息格式:学号+英文空格+姓名+英文空格+课程名称+英文空格+平时成绩+英文空格+期末成绩

实验课程成绩信息包括:学号、姓名、课程名称、每次成绩{在系列-2的基础上去掉了(实验次数),实验次数要和实验课程信息中输入的分项成绩数量保持一致}

实验课程信息格式:学号+英文空格+姓名+英文空格+课程名称+英文空格+第一次实验成绩+...+英文空格+最后一次实验成绩

以上信息的相关约束:

1)成绩是整数,不包含小数部分,成绩的取值范围是【0,100】

2)学号由8位数字组成

3)姓名不超过10个字符

4)课程名称不超过10个字符

5)不特别输入班级信息,班级号是学号的前6位。

2、输出:

输出包含三个部分,包括学生所有课程总成绩的平均分、单门课程总成绩平均分、班级所有课程总成绩平均分。

为避免四舍五入误差,

计算单个成绩时,分项成绩乘以权重后要保留小数位,计算总成绩时,累加所有分项成绩的权重分以后,再去掉小数位。

学生总成绩/整个班/课程平均分的计算方法为累加所有符合条件的单个成绩,最后除以总数。

1)学生课程总成绩平均分按学号由低到高排序输出

格式:学号+英文空格+姓名+英文空格+总成绩平均分

如果某个学生没有任何成绩信息,输出:学号+英文空格+姓名+英文空格+"did not take any exams"

2)单门课程成绩按课程名称的字符顺序输出

课程成绩输出格式:课程名称+英文空格+总成绩平均分

如果某门课程没有任何成绩信息,输出:课程名称+英文空格+"has no grades yet"

3)班级所有课程总成绩平均分按班级由低到高排序输出

格式:班级号+英文空格+总成绩平均分

如果某个班级没有任何成绩信息,输出:班级名称+英文空格+ "has no grades yet"

异常情况:

1)如果解析某个成绩信息时,课程名称不在已输入的课程列表中,输出:学号+英文空格+姓名+英文空格+":"+课程名称+英文空格+"does not exist"

2)如果解析某个成绩信息时,输入的成绩数量和课程的考核方式不匹配,输出:学号+英文空格+姓名+英文空格+": access mode mismatch"

以上两种情况如果同时出现,按第一种情况输出结果。

3)如果解析某个课程信息时,输入的课程性质和课程的考核方式不匹配,输出:课程名称+" : course type & access mode mismatch"

4)格式错误以及其他信息异常如成绩超出范围等,均按格式错误处理,输出"wrong format"

5)若出现重复的课程/成绩信息,只保留第一个课程信息,忽略后面输入的。

6)如果解析实验课程信息时,输入的分项成绩数量值和分项成绩权重的个数不匹配,输出:课程名称+" : number of scores does not match"

7)如果解析考试课、实验课时,分项成绩权重值的总和不等于1,输出:课程名称+" : weight value error"

信息约束:

1)成绩平均分只取整数部分,小数部分丢弃

参考类图(与第一次相同,其余内容自行补充):

fdada4ca193119ee30531ab82ffebbfa_9dbcf4e8-1627-4cf6-8764-cccf44947e2a.png

输入样例1:

在这里给出一组输入。例如:

java 实验 实验 4 0.2 0.3 0.2 0.3
end

输出样例1:

在这里给出相应的输出。例如:

java has no grades yet

输入样例2:

在这里给出一组输入。例如:

java 实验 实验 4 0.2 0.3 0.2
end

输出样例2:

在这里给出相应的输出。例如:

java : number of scores does not match

输入样例3:

在这里给出一组输入。例如:

java 实验 实验 4 0.2 0.3 0.2 0.1
end

输出样例3:

在这里给出相应的输出。例如:

java : weight value error

输入样例4:

在这里给出一组输入。例如:

java 实验 实验 4 0.2 0.3 0.2 0.3
20201116 张三 java 70 80 90 100
end

输出样例4:

在这里给出相应的输出。例如:

20201116 张三 86
java 86
202011 86

输入样例5:

在这里给出一组输入。例如:

java 实验 实验 4 0.2 0.3 0.2 0.3
20201116 张三 java 70 80 90 100 80
end

输出样例5:

在这里给出相应的输出。例如:

20201116 张三 : access mode mismatch
20201116 张三 did not take any exams
java has no grades yet
202011 has no grades yet

设计与分析:

 1. 类结构的修改:

 

 1.1 新建类:

在本次实验中,为了更好地反映成绩信息的复杂性,我对原有的继承关系进行了调整。首先,我新建了两个重要的类,分别是`CourseGrade`类和`SubitemGrade`类。

- **CourseGrade类**:这个类表示整个课程的成绩信息,包含了平时成绩、期末成绩等属性。与之前的继承关系不同,我将成绩信息的继承关系改为了组合关系,即`CourseGrade`类组合了`SubitemGrade`类的实例。

- **SubitemGrade类**:这个类表示分项成绩的信息,包括成绩分值和权重。这样的设计使得每个课程可以包含多个分项成绩,而不仅仅局限于平时成绩和期末成绩。

 1.2 修改类关系:

原先的类图中,成绩类的继承关系较为简单,但在实际应用中,这种继承关系可能不够灵活。通过引入组合关系,我重新设计了类之间的关系。

- **Course类**:原先用于表示课程的类,在新的设计中,它关联了一个`CourseGrade`实例,通过这种组合关系,`Course`类能够更灵活地管理各种类型的成绩信息。

- **Student类**:学生类关联了多个`CourseGrade`实例,这使得学生可以同时参与多门课程,每门课程都有独立的成绩信息。

通过这样的设计,我们实现了更加灵活和可扩展的类结构,使得每个课程的成绩信息可以更细致地进行管理和计算。

在类结构的修改部分,要清晰地说明新增、删除、修改的类,以及类之间的新关系。同时,可以辅以类图或伪代码,更直观地展示类结构的变化。

 2. 继承与组合关系的比较:

2.1 选择组合关系的原因:

在原有的课程成绩统计程序中,我们采用了继承关系来表示不同课程性质和考核方式的差异。然而,在面对更为复杂的成绩信息时,继承关系可能显得过于僵化。因此,我选择将继承关系改为组合关系,以更好地应对不同课程的特殊需求。

 2.2 组合关系的灵活性:

通过引入组合关系,我们使得每个课程可以包含多个分项成绩,而不再局限于继承的单一结构。这种灵活性使得系统更容易适应不同课程性质和考核方式的变化。例如,对于实验课程,可以根据实际需要灵活定义多个实验成绩分项,而不再受到继承结构的限制。

2.3 适应变更的能力:

组合关系使得各个组件更加独立,降低了耦合度,从而增加了系统的可维护性和可扩展性。如果未来需要新增一种课程性质或者引入新的考核方式,可以更轻松地进行修改和扩展,而不必担心对整个继承结构的影响。

2.4 比较:

继承关系示例(修改前):


class ExamCourse extends Course {
//...
}

class LabCourse extends Course {
//...
}

class Student {
List<ExamCourse> examCourses;
List<LabCourse> labCourses;
//...
}
```

组合关系示例(修改后):

```
class CourseGrade {
List<SubitemGrade> subitemGrades;
//...
}

class SubitemGrade {
//...
}

class Course {
CourseGrade courseGrade;
//...
}

class Student {
List<Course> courses;
//...
}
```

通过这样的比较,我们可以清晰地看到组合关系相对于继承关系的优势,特别是在面对复杂的成绩信息和未来变更的情况下,组合关系更加灵活和适应性更强。

3. 类之间的交互:

在实验报告中,描述类之间的交互需要详细说明每个类的职责和功能,以及它们如何相互协作以实现成绩计算和信息管理。以下是一个示例描述,你可以根据你的实际情况进行调整:

 3. 类之间的交互:

 3.1 Course类的职责和功能:

- **Course类**:表示课程的基本信息,包括课程名称、课程性质、考核方式等。在修改后的设计中,它关联了一个`CourseGrade`实例,即课程的成绩信息。

 3.2 CourseGrade类的职责和功能:

- **CourseGrade类**:表示整个课程的成绩信息,包括平时成绩、期末成绩以及分项成绩。与之前的继承关系不同,`CourseGrade`类组合了`SubitemGrade`类的实例,这样就能够更灵活地管理和计算各种类型的成绩。

3.3 SubitemGrade类的职责和功能:

- **SubitemGrade类**:表示分项成绩的信息,包括成绩分值和权重。通过这个类,我们可以为每个课程定义多个分项成绩,适应不同课程的需求。

 3.4 Student类的职责和功能:

- **Student类**:表示学生的基本信息,包括学号、姓名等。它关联了多个`CourseGrade`实例,使得一个学生可以同时参与多门课程,每门课程都有独立的成绩信息。

 3.5 类之间的交互方式:

在计算总成绩时,`Student`对象可以通过访问每门课程的`CourseGrade`实例来获取对应的成绩信息。每个`CourseGrade`实例又包含了平时成绩、期末成绩以及多个`SubitemGrade`实例。通过这样的嵌套关系,系统能够逐层获取和计算各个分项的成绩,最终得到每门课程的总成绩。

通过这种设计,各个类之间的交互变得清晰而有序,每个类都承担了特定的职责,使得系统的结构更加模块化和易于理解。同时,这种设计也提高了系统的可维护性,因为每个类的功能单一,修改或扩展时不容易影响其他部分。

 

在实验报告中描述数据结构与算法的部分,如果使用Java语言,可以按照以下方式进行写作。以下是一个示例描述:

4. 数据结构与算法):

 4.1 数据结构的选择:

在实验中,我选择了一些关键的数据结构来有效地管理和计算成绩信息。

- **ArrayList**:用于存储多个`SubitemGrade`实例,使得每门课程的成绩信息可以动态地扩展和管理。

- **HashMap**:用于存储分项成绩的权重,通过课程的`CourseGrade`实例可以方便地查找和应用相应的权重。

 4.2 算法的设计:

在计算总成绩时,我采用了一种动态调整权重和分值的算法,以适应不同课程性质和考核方式的需求。

- **动态调整权重和分值**:通过在每个`CourseGrade`实例中存储分项成绩的权重和分值,系统能够根据具体的课程情况动态调整计算规则。这样,不同课程可以有不同的计算方式,而不必修改整体算法。

- **成绩计算算法**:通过逐层获取每个分项成绩的分值和权重,使用加权平均的方式计算总成绩。这种算法灵活适用于各种考核方式,例如,对于某些课程可以更加注重期末考核,而对于其他课程可能更注重平时表现。

4.3 示例说明:


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class SubitemGrade {
private double value;
private double weight;

public SubitemGrade(double value, double weight) {
if (!(value >= 0 && value <= 100 && weight >= 0 && weight <= 1)) {
throw new IllegalArgumentException("Invalid value or weight.");
}
this.value = value;
this.weight = weight;
}
}

class CourseGrade {
private List<SubitemGrade> subitemGrades = new ArrayList<>();
private Map<SubitemGrade, Double> weights = new HashMap<>();

public void addSubitemGrade(SubitemGrade subitemGrade, double weight) {
subitemGrades.add(subitemGrade);
weights.put(subitemGrade, weight);
}

public double calculateTotalGrade() {
double totalScore = 0;
double totalWeight = 0;

for (SubitemGrade subitemGrade : subitemGrades) {
totalScore += subitemGrade.getValue() * weights.get(subitemGrade);
totalWeight += weights.get(subitemGrade);
}

return (totalWeight != 0) ? totalScore / totalWeight : 0;
}
}

class Student {
private List<CourseGrade> courses = new ArrayList<>();

public void addCourse(CourseGrade courseGrade) {
courses.add(courseGrade);
}

public double calculateOverallGrade() {
double totalScore = 0;
double totalWeight = courses.size(); // Assuming equal weight for each course

for (CourseGrade courseGrade : courses) {
totalScore += courseGrade.calculateTotalGrade();
}

return (totalWeight != 0) ? totalScore / totalWeight : 0;
}
}

通过这样的数据结构和算法的设计,系统能够灵活地适应不同的成绩管理需求,并且计算总成绩的过程也变得清晰而可控。

 

5. 错误处理和异常情况:

5.1 输入数据验证:

在实验中,我采取了严格的输入数据验证机制,确保输入的成绩数据符合预期的格式和范围。例如,对于分值和权重,我进行了范围检查,防止输入数据超出合理范围。


class SubitemGrade {
private double value;
private double weight;

public SubitemGrade(double value, double weight) {
if (!(value >= 0 && value <= 100 && weight >= 0 && weight <= 1)) {
throw new IllegalArgumentException("Invalid value or weight.");
}
this.value = value;
this.weight = weight;
}
}
5.2 异常处理:

针对可能出现的运行时异常,我在关键的地方实现了异常处理机制,以防止程序崩溃或产生不正确的结果。例如,在计算总成绩的过程中,对除零异常进行了处理。


class CourseGrade {
private List<SubitemGrade> subitemGrades = new ArrayList<>();
private Map<SubitemGrade, Double> weights = new HashMap<>();

public double calculateTotalGrade() {
double totalScore = 0;
double totalWeight = 0;

for (SubitemGrade subitemGrade : subitemGrades) {
totalScore += subitemGrade.getValue() * weights.get(subitemGrade);
totalWeight += weights.get(subitemGrade);
}

try {
return (totalWeight != 0) ? totalScore / totalWeight : 0;
} catch (ArithmeticException e) {
System.err.println("Warning: Total weight is zero. Unable to calculate total grade.");
return 0;
}
}
}

5.3 日志记录:

在遇到错误或异常情况时,我引入了日志记录机制,将相关信息记录下来,有助于后续排查问题。这样即使出现异常,也能更容易地定位和解决问题。


import java.util.logging.Level;
import java.util.logging.Logger;

class CourseGrade {
private static final Logger LOGGER = Logger.getLogger(CourseGrade.class.getName());

// ... Other code ...

public double calculateTotalGrade() {
double totalScore = 0;
double totalWeight = 0;

for (SubitemGrade subitemGrade : subitemGrades) {
totalScore += subitemGrade.getValue() * weights.get(subitemGrade);
totalWeight += weights.get(subitemGrade);
}

try {
return (totalWeight != 0) ? totalScore / totalWeight : 0;
} catch (ArithmeticException e) {
LOGGER.log(Level.SEVERE, "Error calculating total grade: " + e.getMessage(), e);
return 0;
}
}
}

通过这样的错误处理机制,我确保了程序在面对各种输入和运行时情况时能够保持稳定,提高了系统的可靠性和可维护性。

 

 6. 代码可维护性和可扩展性:

 6.1 良好的代码结构:

在整个成绩管理系统中,我采用了清晰的代码结构,按照功能模块进行划分。每个类负责特定的功能,通过良好的命名规范和包结构,使得整个代码库具有直观的组织结构。

com.example.gradesystem
SubitemGrade.java
CourseGrade.java
 Student.java

6.2 注释和文档:

为了提高代码的可读性和可维护性,我在关键的地方添加了注释,解释了每个类、方法的作用和用法。此外,我还可以使用JavaDoc注释来生成详细的文档,方便其他开发者理解和使用我的代码。


* Represents a subitem grade in a course.
*/
class SubitemGrade {

}


* Represents the overall grade for a course.

class CourseGrade {

}


* Represents a student with multiple courses.

class Student {

}
```

 6.3 模块化设计:

我采用了模块化的设计思想,将整个系统划分为多个独立的模块,每个模块负责特定的功能。这样的设计使得各个模块之间的耦合降低,便于单独维护和测试。


class SubitemGrade {

}

class CourseGrade {

}

class Student {
private List<CourseGrade> courses = new ArrayList<>();

public void addCourse(CourseGrade courseGrade) {
courses.add(courseGrade);
}

public double calculateOverallGrade() {

}
}
 6.4 面向对象的思想:

我充分运用了面向对象的思想,通过类和对象的封装,将数据和行为组织成独立的单元。这样的设计使得代码更具灵活性,易于理解和扩展。


class SubitemGrade {
private double value;
private double weight;


}

class CourseGrade {
private List<SubitemGrade> subitemGrades = new ArrayList<>();
private Map<SubitemGrade, Double> weights = new HashMap<>();


}

class Student {
private List<CourseGrade> courses = new ArrayList<>();


}

 6.5 设计模式的运用:

在必要的情况下,我考虑使用设计模式来解决特定的问题,提高代码的可扩展性。例如,可以考虑使用观察者模式来实现成绩变化的通知机制,或者工厂模式来创建不同类型的成绩对象。


// Observer Pattern
interface GradeObserver {
void update(double newGrade);
}

class CourseGrade implements GradeObserver {

@Override
public void update(double newGrade) {
// Handle grade update
}
}

// Factory Pattern
class GradeFactory {
public CourseGrade createCourseGrade() {

}

public SubitemGrade createSubitemGrade() {

}
}

通过以上的设计和实践,我确保了代码的可维护性和可扩展性,使得系统能够轻松地应对未来的需求变化和功能扩展。

实验总结:

在本次实验中,我深入学习了数据结构与算法在Java语言中的应用,探讨了错误处理和异常情况的策略,并关注了代码可维护性和可扩展性的实践。以下是对本阶段学到的内容的总结和反思:

学到了什么:

- **数据结构与算法:** 通过实践,我学会了在Java中选择和使用合适的数据结构,如ArrayList和HashMap。灵活运用算法设计,实现了动态调整权重和分值的功能,提高了系统的适应性。

- **错误处理和异常情况:** 深入理解了Java中的异常处理机制,采用了严格的输入数据验证和日志记录,确保系统在面对异常情况时能够稳定运行。

- **代码可维护性和可扩展性:** 通过良好的代码结构、注释和模块化设计,我确保了代码的可读性和可维护性。运用面向对象的思想和设计模式,提高了代码的灵活性和可扩展性。

 需要进一步学习及研究的地方:

- **进一步学习数据结构与算法:** 深入研究更复杂的算法和数据结构,如图算法、排序算法等,以提高解决实际问题的能力。

- **深入理解异常处理机制:** 进一步学习Java异常处理的最佳实践,包括自定义异常、异常链传递等,以提高代码的健壮性。

- **设计模式的深入应用:** 学习更多的设计模式,并在实际项目中深入应用,以优化代码结构和提高可维护性。

对教师、课程、作业、实验、课上及课下组织方式等方面的改进建议及意见:

- **更多实际项目案例:** 希望课程能够提供更多实际项目案例,让学生能够在实际应用中深入理解和运用所学知识。

- **更加互动的课堂环境:** 提倡更多的互动和讨论,促进学生之间的交流与合作,以加深对知识的理解。

- **反馈机制的加强:** 希望能够有更及时的作业和实验反馈,以便学生能够及时纠正错误并提高学习效果。

- **引入更多实际开发工具和技巧:** 在课程中引入更多实际开发中常用的工具和技巧,如版本控制、持续集成等,以提高实际项目开发的能力。

总的来说,本次实验使我对Java语言的应用有了更深刻的理解,并为我今后在软件开发领域的学习和实践提供了有力的支持。希望能够继续深入学习,不断提升自己的编程技能和综合素养。