pta第三阶段题目集

发布时间 2023-06-27 23:04:11作者: 熊建新

(1)前言

pta第三阶段作业中,主要包含了如下的主要内容:

1.全程贯穿了课程设计的程序,每一次都是上一次的迭代和修改,难度较大,中间涉及到先是类与类之间的多态和继承关系,后面的修改中,转变为了组合的关系,我认为难度较高,相较于之前的菜单类设计还是简单了一些。

2.有一道 统计Java程序中关键词的出现次数 的题目,中间运用到了String类,StringBuilder类,以及新学的map类中的TreeMap,我认为难度也较大,比较考验个人对类中间的方法的熟练程度,以及自身的逻辑思维能力。

3.中间还有几道关于ArrayList类的题目,主要让我学会了其中ArrayList中的方法的使用,以及用ArrayList取代数组的一些好处,难度较为简单,主要是对ArrayList的方法以及使用的初步接触,我认为比数组好用的多。

(2)设计与分析(对题目源码分析,参考power。。有相应的解释和心得)

1.分析oop09题目集中的这道题:

编写程序统计一个输入的Java源码中关键字(区分大小写)出现的次数。

(1)首先先搜索java中的关键字分别有什么,查询过后有如下53个关键字:

"abstract","assert","boolean","break","byte","case","catch",
"char","class","const", "continue","default","do","double","else","enum",                 
"extends","false","final","finally","float","for","goto","if","implements",
"import","instanceof","int","interface","long","native","new","null",
"package","private","protected","public","return", "short","static",
"strictfp","super","switch","synchronized","this","throw","throws",
"transient","true","try","void","volatile","while"

然后用String【】 数组对关键字进行存放。

(2)后利用while循环进行输入并判断,将每行处理过后的字符串放入StringBuilder中,具体操作如下:

while( input.equals(exit)==false) {
stringBuilder.append(input.replaceAll("//.*", " ").replaceAll("\".*\"", " "));//去除两次
input = in.nextLine();
flag=1;
}

(3)

利用Pattern类和Matcher类对所有的关键字进行处理:

输出的时候利用for循环进行遍历,完成输出。

for (i=0;i< map3.length;i++){
String[] map4=map3[i].split("=");
System.out.println(map4[1]+"\t"+map4[0]);
}

 

2.  对第一次的课程成绩设计进行分析

图中给的类图:

必修课、选修课,从考核方式上分为:考试、考察。

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

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

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

首先根据题目要求,获取题目中的关键信息:

1.必修课只能对应考试:考试成绩=总成绩=平时成绩*0.3+期末成绩*0.7;

 选修课对应考试或者考察两种:则若是考试,则总成绩=平时成绩*0.3+期末成绩*0.7;若是考察,则总成绩=1*期末成绩。

所以根据图中所给的类图,自己再加以修改,先构造出抽象成绩类,然后下划两个子类,分别为考试成绩类和考察成绩类。具体设计如下代码:

首先是抽象成绩类设计:

 

abstract class Score {                //包含平时成绩和期末成绩两个属性,以及getter和setter方法。并且含有一个获取总成绩的抽象方法
int usScore=0;
int endScore=0;
public Score() {
}
public Score(int endScore) {
this.endScore = endScore;
}
public Score(int usScore, int endScore) {
this.usScore = usScore;
this.endScore = endScore;
}
public abstract int getScore() ;                     / /抽象方法

 

public void setUsScore(int usScore) {
this.usScore = usScore;
}
public void setEndScore(int endScore) {
this.endScore = endScore;
}
}

 

然后是考察成绩类(继承抽象成绩类)

 

class Kaocha extends Score {               // 需要复写父类中获得成绩的方法
public Kaocha(){
super();
}
public Kaocha(int endScore) {
super(endScore);
}
public int getScore(){
return this.endScore;
}
}

然后是考试成绩类

 

class Kaoshi extends Score {           // 需要复写父类中获得成绩的方法
public Kaoshi(){super();}
public Kaoshi(int endScore) {
super(endScore);
}
public Kaoshi(int usScore, int endScore) {
super(usScore, endScore);
}
public int getScore(){
return (int) (this.usScore*0.3 +this.endScore*0.7);
}
}

其次根据题目要求,再次抽取关键信息:

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

根据要求,设计一个课程记录类,也就是课表类,包含了应有的课程:

1.利用ArrayList,能够有效对课表内的课程进行增删改查,其中这个list放在课程类中(由于需要判断加入的课程是否存在,所以需要构造一个方法来判断,所以不能单独使用一个list作为课表,而是需要将list放入课程类里,课程类里面存在方法判断是否可以add)。具体代码的实现如下图

class CourseRecord {
ArrayList<Course> courseArrayList=new ArrayList<>();
Course addCourse(String courseName,String courseChoice,String courseTest){        //添加一道课程信息,可以判断是否有重复的课程从而选择不加入相同的课程。
for (Course course: courseArrayList) {
if (course.getName().equals(courseName)) {
course.setChoice(courseChoice);
course.setTest(courseTest);
return course;
}
}
Course course=new Course(courseName,courseChoice,courseTest);
courseArrayList.add(course);
return course;
}
}

然后再根据题目要求,输入学生信息 学生的一门课程 以及该课程的成绩。

关键信息:

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

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

3)学号由8位数字组成

4)姓名不超过10个字符

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

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

输入学生的各个信息时需要判断学生的学号 姓名 成绩 是否正确(班级通过学号的前六位展示出来)

所以要根据要求,设计各个类:

首先设计一个类,能够包含各个班级,其中加入ArrayList来存储各个班级,构造方法对班级进行加入list中,具体设计如下:

class allChoice {
ArrayList<ClassOne> classes=new ArrayList<>();                     //存储班级的list
public ArrayList<ClassOne> getClasses() {
return classes;
}
ClassOne addClass(int num){                        //用来添加新的班级到list的方法,可以判断是否有重复的班级,有的话则不加班级。
for (ClassOne class1: classes) {
if (class1.getNum()==num) {
return class1;
}
}
ClassOne class1=new ClassOne(num);
classes.add(class1);
return class1;
}
}

然后设计班级类,属性包括班级号,班级的总分,以及班级的平均分,还包括一个ArrayList来存入该班级的学生,以及一个加入新的学生的方法,具体代码如下图:

class ClassOne {
int num=0;//班级号
int score=0;
int numOfScore=0;
ArrayList<Student> students=new ArrayList<>();               //用来存放学生的list
public ClassOne(int num) {
this.num = num;
}
public int getNum() {
return num;
}
boolean hasScore=true;
void setHasScore(){
int score=0;
for(int i=0;i<students.size();i++){

}
for(int i=0;i<students.size();i++) {
this.numOfScore+=students.get(i).courses.size();
score += students.get(i).averageScore;
}
this.score=score;
if(this.score==0) hasScore=false;
}
int getScore(){
return this.score/this.numOfScore;
}
Student addStudent(int numBan,String name){                 //用来加入新的学生的方法。
for (Student student: students) {
if (student.getNum()==numBan) {
return student;
}
}
Student student=new Student(numBan,name);
students.add(student);
return student;
}
}

 

然后在设计学生类,其中属性包括姓名 学号 所有课的平均分,以及一个ArrayList,用来存放该学生含有的课程,并且含有两个加入学生的方法(两个方法的参数不同,由于成绩分为考试和考察所以当录入学生成绩时课程是考察的话,只有期末成绩一个参数,当是考试的时候,有平时成绩和期末成绩两个参数)。由于在输出的时候需要输出各个课程的平均成绩以及各个班级的平均成绩,所以两个方法中都涉及到了对课程成绩的修改以及对班级成绩的修改。

class Student {
ArrayList<Course> courses=new ArrayList<>();
String name="";//姓名
int num=0;//学号
int averageScore=0;//学生所有课的平均分
boolean isTest=true;
public void setAverageScore() {
int score=0;
for(int i=0;i<courses.size();i++){
score+=courses.get(i).getScore();
}
this.averageScore=score;
}
public int getAverageScore(){
return this.averageScore/courses.size();
}
public Student(int num, String name) {
this.name = name;
this.num = num;
}

void addCourse(CourseRecord courseRecord,String name1,int score1){         //当是一个成绩的时候,用该方法加入学生,
Course course;
int flag=0;
for(Course course1:courseRecord.courseArrayList){
if(name1.equals(course1.name)){
if(course1.isExist==false) break;
course=new Course(course1.name,course1.choice,course1.test);
if(course.test.equals("考试")) {
if (course.score.usScore == 0) {
this.isTest = false;
break;
}
}
course.score.setEndScore(score1);
this.courses.add(course);
courseRecord.courseArrayList.get(flag).renshu++;
courseRecord.courseArrayList.get(flag).courseSumScore+=score1;
break;
}
flag++;
}
}
void addCourse(CourseRecord courseRecord,String name,int score,int score2){       //两个成绩时,则用该方法加入学生
Course course;
int flag=0;
for(Course course1:courseRecord.courseArrayList){
if(course1.name.equals(name)){
course=new Course(course1.name,course1.choice,course1.test);
course.score.setUsScore(score);
course.score.setEndScore(score2);
courseRecord.courseArrayList.get(flag).renshu++;                                                 //记录该课程的人数
courseRecord.courseArrayList.get(flag).usSumScore+=score;
courseRecord.courseArrayList.get(flag).endSumScore+=score2;
courseRecord.courseArrayList.get(flag).courseSumScore+=score*0.3+(score2*0.7);
this.courses.add(course);
break;
}
flag++;
}
}
public int getNum() {
return num;
}
}

然后构造一个Course类,属性包含课名,必修选修,考试考察,还包括一个抽象成绩类Score(用来记录某个学生该成绩的分数)还把包括该课程的平时成绩均分,期末成绩均分以及总成绩均分(用来记录课程的成绩),这样在输出的时候,既能利用Score输出学生课程成绩,又能利用自带的int属性出输出课程的总成绩以及均分,具体代码如下图所示:

class Course {
String name="";
String choice="";
String test="";//必修只能是考试,选修考试或考察;
Score score;
int renshu=0;
int usSumScore=0;//一个课程平时平均分
int endSumScore=0;//一个课程期末平均分
int addScore=0;//考试总分
int courseSumScore=0;//一个考察课程的总平均分
boolean isExist=true;//通过必修不能考察判断能否录入课程
boolean isTest=true;
boolean havePerson(){
if(this.renshu==0) return false;
else return true;
}
public int getCourseSumScore(){
return courseSumScore/this.renshu;
}
public int getUsSumScore() {
return usSumScore/this.renshu;
}
public int getEndSumScore() {
return endSumScore/this.renshu;
}
public int getAddScore() {
return addScore/this.renshu;
}
public int getScore(){
return this.score.getScore();
}
public Course(){}
public Course(String name, String choice, String test) {
this.name = name;
this.choice = choice;
this.test = test;
if(choice.equals("必修")&&test.equals("考察"))
this.isExist=false;
else if(choice.equals("必修")||(choice.equals("选修")&&test.equals("考试"))){
this.score=new Kaoshi();
}else if(choice.equals("选修")&&test.equals("考察"))
this.score=new Kaocha();
}
public void setName(String name) {
this.name = name;
}
public void setChoice(String choice) {
this.choice = choice;
}
public void setTest(String test) {
this.test = test;
}
public String getName() {
return name;
}

}

主函数的设计:

主函数主要是是对输入和输出进行编写:

首先是输入,这里我使用while循环进行输入的控制,以及各个信息的录入,其中使用split将输入的一行字符串,转换成一个字符数组,这样方便各个不同信息的录入。如下图所示

然后利用对数组长度的大小用if语句进行判断,如果长度等于三,则是将课程录入到课程表中(根据题目要求,还要用if语句判断课程名字长度,不能大于10),如下图所示:

如果长度等于4,则为录入学生成绩,并且是课程性质为选修以及考察的课程成绩的录入(包括对新班级的录入以及新学生的录入),代码如下图所示

由于题目中课程均分和班级均分的成绩输出要求顺序输出,所以需要分别对课程list、班级list以及学生list进行排序,用到了Collection中的sort方法,其中由于学生list存在在各个班级中,所以需要利用afor循环对学生的成绩进行排序,代码的实现如下图所示(三个collection分别对课程,班级,学生进行排序)

 

 (3).采坑心得:

1.由于在后两次迭代课程成绩中,需要分别加上实验课以及将之前成绩之间的继承关系改为组合,中间便出现了许多问题,以及也明白了其中的问题所在:

比如在判断录入实验课的课程信息是否正确,当时没有想到使用正则表达式,中间便出现了使用很多if语句,正确使用的代码如下:

if (!array[0].matches("[0-9]{8}") || array[1].length() > 10 || (n < 0 || n > 100))

2.在给ArrayList排序的过程中,有题目要求按照list中的个别属性进行降序排序,当时没有注意,还是将正常的Collection的sort方法套用了进去,发现还是升序输出,结果发现应该将compare方法中的返回值做修改,改为后一个参数的值减去前一个的(前一个设为o1,后一个为o2,那么就是o2.属性-o1.属性),代码如下图:

 

(4)改进建议:对相应题目的编码改进给出自己的见解,做到可持续改进。

这里我选择了这阶段最难得的题目来讲,也就是课程成绩的迭代,共三次,其中题目中所给的类图如下:

 然后我稍作分析,发现我按照从小到大的顺序放在ArrayList中,具体实现为:

先构造最基础的单元,也就是课程,然后每个学生可以有很多个课程的成绩,所以构造学生类,将课程放入学生类中的list中,然后每个班级又有很多个学生,所以再构造班级类,将学生类放入到班级类中的list中。然后一个年级或者一个学校有很多个班级,所以创造一个allClass类,然后将班级放在该类的list中。

附加:由于课程需要根据输入逐个录入到课程表中,所以同时需要一个课程表类,将课程放在课程表中的list中。

这里注意到课程类会被同时放在学生中的list以及课程表中的list,这样在分别输出成绩的时候考虑到会输出相同的值(但其实这样是错误的),所以在成绩的录入的时候,录给学生的成绩放在学生的Score对象中(这里的对象里面有成绩属性),给录课程的成绩则放在课程自己创建出的一个int属性中,这样可以分别输出,下图是我自己设计的类图:

 

 

(5)总结:

1.在课程代码中使用了许多类与对象之间的关系,也是使用到了继承和多态,对面向对象程序设计有了进一步的理解。

2.学会ArrayList的使用,并且能熟练的将其运用到代码中。

ArrayList 最大的优势就是可以将很多数组操作的细节封装起来。比如前面提到的数组插入、删除数据时需要搬移其他数据等。另外,它还有一个优势,就是支持动态扩容。

数组本身在定义的时候需要预先指定大小,因为需要分配连续的内存空间。如果我们申请了大小为 10 的数组,当第 11 个数据需要存储到数组中时,我们就需要重新分配一块更大的空间,将原来的数据复制过去,然后再将新的数据插入。

如果使用 ArrayList,我们就完全不需要关心底层的扩容逻辑,ArrayList 已经帮我们实现好了。每次存储空间不够的时候,它都会将空间自动扩容为 1.5 倍大小。
例如:增删改查。如下:

public boolean remove(Object o) 删除指定的元素,返回删除是否成功
public E remove(int index) 删除指定索引处的元素,返回被删除的元素
public E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
public E get(int index) 返回指定索引处的元素
public int size() 返回集合中的元素的个数

3.

学会了hashmap的用法,具体如下:

put(key, value):将指定的键值对添加到HashMap中。

get(key):返回给定键对应的值。

remove(key):从HashMap中删除指定键对应的键值对。

containsKey(key):判断HashMap中是否包含指定的键。

containsValue(value):判断HashMap中是否包含指定的值。

size():返回HashMap中键值对的数量。

keySet():返回HashMap中所有键的集合。

values():返回HashMap中所有值的集合。

entrySet():返回HashMap中所有键值对的集合。

clear():删除HashMap中所有键值对。

2.对本门课的建议:

首先先对目前的教学方法评价,我的评价是中规中矩中,又带点逼的感觉,中规中矩是觉得上课讲的内容啊,以及实验课要求我们完成的一些东西,我认为都是比较正常的,没有什么太大的压迫感,主要是pta中的练习 ,特别是题目集中菜单的迭代,以及课程的迭代,说实话,我认为可能也确实锻炼到我们了吧,但是感觉量实在有点大,有时候确实喘不过气来。边学边练我觉得效率比较高,但是我认为,更重要的事对面向对象的整体理解,以及老师讲到的面向对象编程,而不是面向过程,这样效率就低,也学不到什么东西。特别是课上,我觉得其实学到的东西很少,很多东西靠自己能够在网上学到,有时候讲的东西嘛,又听不懂,消化不了,导致根本等于没上课。

然后我对本课程目前最大的建议,就是希望老师能教给我们如何系统的去学习java或者更多语言,以及精通他们,当然精通的过程肯定是靠个人的,但是引导我们的,老师还是非常关键的,毕竟目前没有前车之鉴,希望老师在方法上能够帮助到我们,叫我们应该怎么学,让我们提高学习效率,不然最后花了很多时间,发现是无用功,到最后竹篮打水一场空。我们愿意在专业上面多花时间,但也希望不是所有弯路都摆在我们面前让我们一个个得走过。