题目集1-3总结

  1. 前言

  2. 设计与分析

  3. 踩坑心得

  4. 踩坑心得

  5. 改进建议

  6. 总结

前言


知识点
每次题目集在小题上都能给出新的知识去让我们学习,包括类的构造,类的引用,数组的基本运用,对象的构造,更包括对属性状态的应用,尽管以我们现在的水平还无法真正理解到属性的状态对程序乃至工程整体的作用。另外对于初次接触这样较大型的Java程序时我还是很不适应,好在菜老师的新手指导给予了很大的帮助:不过迭代的难度依旧不小😄
UML图的设计: 确定需求-识别类-定义类的属性集方法-建立类之间的关系-绘制类图-完善类图-验证和改进。
也包括对某些常用类的了解和List集合类型: 例如其中的ListLink集合类型,其中的sort方法——第一次了解重构,time类的应用,通过小练习题中对这些小语法的熟练对我
在迭代题目程序的编写具有很大帮助。
面向对象的设计原则: 这是每个初学Java的人都听说其是编写程序时最为重要,但真正上手时又弃之如敝履的东西。它主要包括七大设计原则:

  • 单一职责原则(Single Responsibility Principle,SRP)
  • 开放-封闭原则(Open-Closed Principle,OCP)
  • 里氏替换原则(Liskov Substitution Principle,LSP)
  • 接口隔离原则(Interface Segregation Principle,ISP)
  • 依赖倒置原则(Dependency Inversion Principle,DIP)
  • 最少知识原则(Least Knowledge Principle,LKP,又称迪米特法则,Law of Demeter,LoD)
  • 合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
    这里隆重给出
    以示作为初学者歉意.....

.
.
.

设计与分析

1.对于第一次题目集最后一道题的分析

初见大boss,才刚开始便觉得无法掌控,主要问题是题目的长度相比于之前的题目更长,之前的程序更多的是对算法和类方法的使用,而这次确是着重于设计之上
一开始,类的设计在蔡柯老师的帮助下逐步完成,完成实体类的编写与照抄无异,这一步骤较为顺利。
之后,对输入数据的匹配,在运用正则表达式时,着实吃了不少苦头,好在此时对输入的数据没有太多变化要求,然而这却是这道题未达到满分的原因。
第一次题目集最后一题
第一次题目集最后一题类图

点击查看代码
import  java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Question{
    private int title = 0;
    private String  q_information = "";
    private  String stander_answer = "";
    public   Question(){

    }
    public boolean judge1(String answer){ //判断   标答 是否等于 小明答
        if(Objects.equals(this.stander_answer, answer)){
            return true;
        }
        else{
            return false;

        }
    }

    public String getStander_answer() {
        return stander_answer;
    }

    public void setStander_answer(String stander_answer) {
        this.stander_answer = stander_answer;
    }

    public String getQ_information() {
        return q_information;
    }

    public void setQ_information(String q_information) {
        this.q_information = q_information;
    }

    public int getTitle() {
        return title;
    }

    public void setTitle(int title) {
        this.title = title;
    }

}
class Paper{
    private  int  number = 0;

    private Question[]  questions ;
    public Paper(int Q_count){
        this.number = Q_count;
        this.questions  = new Question[Q_count+1];
        for(int i = 0;i<=Q_count;i++){
            questions[i] = new Question();
        }
    }//初始化 题目列表

    public boolean judge2(String answer ,int title){
        return Objects.equals(answer, questions[title].getStander_answer());
    }

    public String seek(int title){
        for(int i=0;i<this.number;i++){
            if(title == questions[i].getTitle()){
                return  questions[i].getStander_answer();
            }
        }
        return "";
    }

    public void setQuestions(int title , String sentence,String answer) {
        // 搜索存储对应title的题目
        this.questions[title].setStander_answer(answer);
        this.questions[title].setTitle(title);
        this.questions[title].setQ_information(sentence);
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }
    public Question[] getQuestions() {
        return questions;
    }
    // 这里 为试卷对象输入 所有  题目实例
// 如 列表中  questions[2] :  title=2;


}

class Script{
    private  Paper paper ;

    private String[]  answer_list ;
    //答案列表是依照题号顺序的
    private boolean[] judge_list ;


    public Script(int Q_count){
        paper = new Paper(Q_count);            // 完成对试卷的初始化
        answer_list = new String[Q_count+1];
        for (int i = 0; i <=Q_count; i++) {
            answer_list[i] = "";
        }                                     //完成对答案列表的初始化
        judge_list = new boolean[Q_count+1];
        Arrays.fill(judge_list, false);   //完成对批改列表的初始化
    }

    public boolean judge3(int title){//第title题是否为第title答案
        if(Objects.equals(this.answer_list[title],paper.getQuestions()[title].getStander_answer())){
            return true;
        }
        return  false;
    }


    public void  to_printf(int title){
        System.out.println(paper.getQuestions()[title].getQ_information() + "~" + answer_list[title]);
    }

    public void set1_answer(int  title,String answer){
        this.answer_list[title] = answer;
    }//此函数用于初始化答卷类 里的答案列表 需用循环完成


    public Paper getPaper() {
        return paper;
    }

    public void setPaper(Paper paper) {
        this.paper = paper;
    }
    public String[] getAnswer_list() {
        return answer_list;
    }

    public void setAnswer_list(String[] answer_list) {
        this.answer_list = answer_list;
    }

    public boolean[] getJudge_list() {
        return judge_list;
    }

    public void setJudge_list(boolean[] judge_list) {
        this.judge_list = judge_list;
    }


}

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int questions_count = scan.nextInt();
        scan.nextLine(); // 消耗掉换行符
        Script script = new Script(questions_count);


        int  is_title = 1;
        String  Qsentence = "";
        String   stander_answer = "";
        String line_paper = "";
        String line_script = "";
        String my_answer = "";
        while(true){
            //输入题号
            for(int i = 1;i<=questions_count;i++){
                line_paper = scan.nextLine();
                is_title =  getlinecount(line_paper ); // 题号
                Qsentence = getQsentence(line_paper);// 问题
                stander_answer = getanswer(line_paper);// 标准答案
                script.getPaper().setQuestions(is_title,Qsentence,stander_answer);
               /* Script.getPaper().getQuestions()[is_title].setTitle(is_title);
                Script.getPaper().getQuestions()[is_title].setQ_information(Qsentence);
                Script.getPaper().getQuestions()[is_title].setStander_answer(stander_answer);//此时 questions[2] : 题号 2*/
            }//初始化题目列表
            
            for(int i = 1;i<=questions_count;i++){ line_script = scan.next();
                my_answer = getanswer(line_script);//  我的答案
                script.set1_answer(i,my_answer);
              //   line_script = line_script.replaceFirst("#A","");

            }// 初始化答案列表篇
            String ending = scan.nextLine();
            if(Objects.equals("end",ending)){
                break;
            }
            break;

        }


        for(int i = 1;i<=questions_count;i++){
            System.out.printf("%s~%s\n",script.getPaper().getQuestions()[i].getQ_information(),script.getAnswer_list()[i]);
        }
        for(int i = 1;i<=questions_count;i++){

            System.out.print(script.judge3(i));
            if(i!=questions_count){
                System.out.print(" ");
            }
        }

    }
    static public   int getlinecount(String line ){
        Pattern r = Pattern.compile("#N:\\s*(\\d+)");
        // 现在创建 matcher 对象
        Matcher m = r.matcher(line);
        if (m.find( )) {
            int number = Integer.parseInt(m.group(1));
            return number;
        }
        else{
            return 0;
        }
    }
    static public   String getQsentence(String line ){
        Pattern r = Pattern.compile("#Q:\\s*(.+?) #A:");
        // 现在创建 matcher 对象
        Matcher m = r.matcher(line);
        if (m.find( )) {

            return m.group(1).replace("\\s+","");
        }

        else{
            return "false";
        }

    }
    static public   String getanswer(String line ){
        Pattern r = Pattern.compile("#A:(\\w+)");
        
        // 现在创建 matcher 对象
        Matcher m = r.matcher(line);
        if (m.find( )) {
            return m.group(1);

        }
        else{
            return "false";
        }

    }

}

这里并没有设计专门的控制类去实现调用各个实体类,进行题目要求的输出以及运算匹配,以下是对主方法中语句作用的解释:
1.Scanner scan = new Scanner(System.in);:创建一个Scanner对象,用于从控制台读取用户的输入。

  1. int questions_count = scan.nextInt();:从用户输入中读取题目数量。

3.scan.nextLine();:消耗掉换行符,以便后续的输入。

  1. Script script = new Script(questions_count);:创建一个Script对象,该对象用于管理题目和答案信息。

  2. 接下来是一个while循环,该循环用于读取用户输入的题目信息和答案信息,并将其添加到Script对象中。

  3. 内层的第一个for循环用于读取题目信息,包括题号、问题和标准答案,并将其添加到Script对象中。

  4. 内层的第二个for循环用于读取答案信息,并将其添加到Script对象中。

  5. 如果用户输入了"end",则跳出循环。

  6. 接下来的两个for循环分别用于输出题目和答案信息,并调用judge3方法来判断答案的正确性。

  7. getlinecount、getQsentence和getanswer方法分别用于从输入的字符串中提取题号、问题和答案信息,并返回相应的值。

最为主要的设计是,script类依赖Paper,Paper依赖Question,即
script->Paper,,Paper->Question;这就使得每次对实体类的调用都要通过调用答卷类从而使得答卷类的方法,属性是复杂而冗长的。

像一窝大杂烩,回头来看只觉得这代码真丑。。。当然这三次代码都丑

2.对于第二次题目集最后一道题的分析

第一次题目集的迭代一体现我初见较大Java程序的,而对代码的准确度把握有不足,而这一次却是体现对整个程序的的设计,架构的不足。未满足开闭,单一职责,厘米特等法则的情况下,导致了整个程序没有足够的健壮性以及包容,稳定性。由于之前未考虑类之间的耦合性的高低,这迭代二的编写只能从头来过,这无疑是漫长而痛苦的。这里给出UML来对类的设计进行具体的分析:

点击查看代码
import  java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Question implements  Comparable<Question>{

    private int title = 0;
    private String  q_information = "";
    private  String stander_answer = "";
    private  int scores = 0;
    public   Question(){

    }
    @Override
    public int compareTo(Question other) {
        //按价格进行比较
        return Integer.compare(this.title, other.title);
    }

    public boolean judge1(String answer){ //判断   标答 是否等于 小明答
        if(Objects.equals(this.stander_answer, answer)){
            return true;
        }
        else{
            return false;

        }
    }

    public String getStander_answer() {
        return stander_answer;
    }

    public void setStander_answer(String stander_answer) {
        this.stander_answer = stander_answer;
    }

    public String getQ_information() {
        return q_information;
    }

    public void setQ_information(String q_information) {
        this.q_information = q_information;
    }

    public int getTitle() {
        return title;
    }

    public void setTitle(int title) {
        this.title = title;
    }

    public int getScores() {
        return scores;
    }

    public void setScores(int scores) {
        this.scores = scores;
    }
}//00000000000000000000000000000000000000000000000000000000000000000000000
class Paper{
    private int title = 1;
    private int paper_length = 0;
    private LinkedList<Question> question_list = new LinkedList<>();


    public Paper(int P_title){
         this.title = P_title;
         Question question = new Question();
         this.question_list.add(question);//占用第0个位置
        }
    //初始化 题目列表

    public boolean judge2(String answer ,int title){
        return Objects.equals(answer, question_list.get(title).getStander_answer());
    }


    public void setAquestion(int title , String sentence, String answer,int sorce) {
        Question question = new Question();
        question.setStander_answer(answer);
        question.setTitle(title);
        question.setQ_information(sentence);
        question.setScores(sorce);
        this.question_list.add(question);
        // 搜索存储对应title的题目
    }
    public Question seek(int title){
        for(int i=0;i<this.question_list.size();i++){
            if(title == this.question_list.get(i).getTitle()){
                return  this.question_list.get(i);
            }
        }

        return null;
    }
    public int getTitle() {
        return title;
    }

    public void setTitle(int title) {
        this.title = title;
    }
    // 这里 为试卷对象输入 所有  题目实例
// 如 列表中  questions[2] :  title=2;
    public LinkedList<Question> getQuestion_list() {
        return question_list;
    }

    public void setQuestion_list(LinkedList<Question> question_list) {
        this.question_list = question_list;
    }

    public int getPaper_length() {
        return paper_length;
    }

    public void setPaper_length(int paper_length) {
        this.paper_length = paper_length;
    }
}//11111111111111111111111111111111111111111111111111111111111111111111111111111
class  ques_answer implements  Comparable<ques_answer>{
    private int  title = 1;
   private  String answer = "";
    private int score = 0;
    public ques_answer(String answer, int score) {
        this.answer =answer;
        this.score =score;
    }
    public ques_answer() {

    }
    @Override
    public int compareTo(ques_answer other ){
        //按价格进行比较
        return Integer.compare(this.title, other.title);
    }
    public String getAnswer() {return answer;}
    public void setAnswer(String answer) {this.answer = answer;}
    public int getScore() {return score;}
    public void setScore(int score) {this.score = score;}

    public int getTitle() {return title;
    }

    public void setTitle(int title) {this.title = title;}
}//222222222222222222222222222222222222222222222222222222222222222222222222222
class Script implements  Comparable<Script>{    //答卷类
    @Override
    public int compareTo(Script other){
        return Integer.compare(this.title,other.title);
    }
    private int title = 1 ;
    private  Paper paper ;
    private LinkedList<ques_answer> answer_list = new LinkedList<>();
    //答案列表是依照题号顺序的
    private  boolean [] judge_list = new boolean [100];
    
    public Script(int title){
        paper = new Paper(title);            // 完成对试卷的初始化
        Arrays.fill(judge_list, false);   //完成对批改列表的初始化
        ques_answer answer = new ques_answer();
        this.answer_list.add(answer);       //占用第0个位置
    }

    public boolean judge3(int title){//第title题是否为第title答案
        if(Objects.equals(this.answer_list.get(title).getAnswer(),paper.getQuestion_list().get(title).getStander_answer())){
            return true;
        }
        return  false;
    }


    public void  to_printf(int title){
        System.out.println(paper.getQuestion_list().get(title).getQ_information() + "~" + this.answer_list.get(title).getAnswer()+"~"+judge3(title));
    }

    public void set1_answer(int  title,String answer){
       ques_answer answer1 = new ques_answer(answer,title);
       answer_list.add(answer1);
    }//此函数用于初始化答卷类 里的答案列表 需用循环完成


    public Paper getPaper() {
        return paper;
    }

    public void setPaper(Paper paper) {
        this.paper = paper;
    }
    public boolean[] getJudge_list() {
        return judge_list;
    }

    public void setJudge_list(boolean[] judge_list) {
        this.judge_list = judge_list;
    }


    public int getTitle() {
        return title;
    }

    public void setTitle(int title) {
        this.title = title;
    }
    public LinkedList<ques_answer> getAnswer_list() {return answer_list;}

    public void setAnswer_list(LinkedList<ques_answer> answer_list) {this.answer_list = answer_list;}

}


public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String   stander_answer = "";//问题类题目信息  #N:1 #Q:1+1= #A:2

        ArrayList<Script> scripts = new ArrayList<>();//有多少答卷有多少试卷

        String line_get = "";//输入的每一行,作为原始行数据
     Script script0 = new Script(1);    //答卷信息存储中介
        scripts.add(script0);//占用第0个位置
    // LinkedList
        while( true){
            line_get = scan.nextLine();
             if(Objects.equals("end",line_get)){
                 break;
             }//结束条件
            String type = type(line_get);
             switch (type){
                 case "#N:":input_queslist(script0,line_get);break;
                 case "#T:":input_text(script0,line_get);break;
                 case "#S:":input_script(script0,line_get,scripts);break;
             }
        }//初始化题目列表 //题目信息

        for (int i=1;i<scripts.size();i++) {  //当存在一个有效元素时它的长度为2
            warning(scripts.get(i));
        }//判断是否满分    1试卷总分警示
        if(script0.getTitle()!=script0.getPaper().getTitle()){
            System.out.println("The test paper number does not exist");
           return;
        }
        for (int k=1;k<scripts.size();k++) {
            Script a = scripts.get(k);
            for(int i =1;i<a.getPaper().getPaper_length();i++) {
                if(!Objects.equals(a.getAnswer_list().get(i).getAnswer(), "")) {
                    System.out.println(a.getPaper().getQuestion_list().get(i).getQ_information() + "~" + a.getAnswer_list().get(i).getAnswer()+"~"+a.judge3(i));
                }
                else {
                    System.out.println("answer is null");
               }
            }          //   2答卷信息
            int all = 0;
            for(int i =1;i<a.getPaper().getPaper_length();i++){
                if(i<a.getAnswer_list().size()-1) {
                    System.out.print(a.getAnswer_list().get(i).getScore()+" ");
                }
                else {
                    System.out.print(a.getAnswer_list().get(i).getScore()+"~");
                }
                all = all + a.getAnswer_list().get(i).getScore();
            }
            System.out.println(all);
        }
    }
    //存储问题链表
   static   public void input_queslist(Script script,String line){
        Question question1 = new Question();
        Pattern pattern = Pattern.compile("#N:(\\d+) #Q:(.+?) #A:(\\w+)");
        Matcher m = pattern.matcher(line);
        while(m.find()){
            int t = Integer.parseInt(m.group(1));
            question1.setTitle(t);
            question1.setQ_information(m.group(2));
            question1.setStander_answer(m.group(3));
        }
        script.getPaper().getQuestion_list().add(question1);
    }
    static  public void input_text(Script script,String line){//#T:1 3-5 4-8 5-

        String [] a = line.split(" ");
        script.getPaper().setPaper_length(a.length);
       // Collections.sort(script.getPaper().getQuestion_list());
           Pattern pattern = Pattern.compile("(\\d+)");
           Pattern pattern1  = Pattern.compile("(\\d+)-(\\d+)");
        Matcher m = pattern.matcher(a[0]);
        while(m.find()) {
            script.getPaper().setTitle(Integer.parseInt(m.group(1)));//设置编号为1的试卷
        }
          for(int i = 1;i< a.length;i++){ Matcher m1 = pattern1.matcher(a[i]);
              while(m1.find()) {
                  //设置题目title总分为 score
                  script.getPaper().seek(Integer.parseInt(m1.group(1))).setScores(Integer.parseInt(m1.group(2)));
              }
          }
    }
    static public void input_script(Script script,String line,ArrayList<Script> scripts){//#S:1 #A:5 #A:22

         while (script.getAnswer_list().size()!=script.getPaper().getPaper_length()){
             ques_answer answer_null = new ques_answer();
             script.getAnswer_list().add(answer_null);
         }
         //使答卷列表长度等于问卷列表长度

         Script script1 = new Script(1);

        String [] a = line.split(" ");
        Pattern pattern = Pattern.compile("(\\d+)");
        Pattern pattern1  = Pattern.compile("#A:(\\d+)");
        Matcher m = pattern.matcher(a[0]);
        while(m.find()) {
            script.setTitle(Integer.parseInt(m.group(1)));//设置此答卷的标号
        }
        Matcher m1 = pattern1.matcher(line);
        int k = 1;
        while(m1.find()){
             script.getAnswer_list().get(k).setAnswer(m1.group(1));
             if(script.judge3(k)){
                 script.getAnswer_list().get(k).setScore(script.getPaper().getQuestion_list().get(k).getScores());
             }
             else{
                 script.getAnswer_list().get(k).setScore(0);
             }
             k++;
        }
        script1=script;
        scripts.add(script1);
    }
    static  public void warning(Script script){
        int all_grade = 0;
        for(Question q:script.getPaper().getQuestion_list()){
              all_grade = all_grade + q.getScores();
        }
         if(all_grade==100){
             return;
         }
         else{
             System.out.println("alert: full score of test paper"+script.getPaper().getTitle()+" is not 100 points");
         }
    }



 static   public String type(String line){
        if(line.contains("#N:")){
            return "#N:";
        }
        if(line.contains("#T:")){
            return "#T:";
        }
        if(line.contains("#S:")){
            return "#S:";
        }
        return "";
    }

}
主要问题:这里最为关键的,是依旧犯下了与迭代一相同的错误,没有降低类之间的耦合性,更没有真正了解到单一职责原则。没有在编写程序时,正确了解,清晰题目输入输出的需求,各个数据之间关系,而没有将题库,试卷这两个问题类的容器类分开,而导致程序混乱。

千里之堤,溃于蚁穴

对迭代二的设计,考虑到多套信息的输入,想要对每一个存储信息类,定义一个对应的信息存储中介来临时存储每类信息并把他们加进各个类型数据的容器存储类链表中,然而此处引发了一个巨大的bug,使得解决这个问题只有重新主函数的构造,亦或者使用深拷贝。当然这里没用成。具体放在踩坑心得上讲。
考虑到单项多步调用会造成许多问题,这里将script中的Paper单独拿出来简化了类之间的关联,降低了类之间的耦合性。
对链表排序的设计,应用了Comparable接口,对链表内的元素进行排序。这里用了LinkdList以及ArrayList的容器类类型。
下面是对主方法语句的分析:
解释一下其中的语句:

  • String lineGet = "";: 这行代码声明并初始化了一个字符串变量lineGet,用于存储从命令行读取的输入内容。

  • Script script0 = new Script(1);: 这行代码创建了一个名为script0的Script对象,并传入了一个整数参数1。

  • scripts.add(script0);: 这行代码将刚创建的script0对象添加到了scripts列表中。

  • Scanner scan = new Scanner(System.in);: 这行代码创建了一个Scanner对象scan,用于从标准输入流(命令行)中读取用户的输入。

  • while (true) { ... }: 这是一个无限循环,程序将在此处等待用户的输入,并根据输入内容执行相应的操作,直到用户输入了"end",才会退出循环。

  • lineGet = scan.nextLine();: 这行代码读取用户输入的下一行内容,并将其存储在lineGet变量中。

  • if (Objects.equals("end", lineGet)) { break; }: 这行代码检查用户输入的内容是否等于"end",如果是,则退出循环,结束程序。

  • String type = getType(lineGet);: 这行代码调用了一个名为getType的方法,根据输入的内容判断其类型,并将结果存储在type变量中。

  • switch (type) { ... }: 这是一个switch语句,根据type变量的值执行不同的操作。根据输入的类型,程序将调用不同的方法来处理输入的内容。

  • for (int i = 1; i < scripts.size(); i++) { warning(scripts.get(i)); }: 这是一个for循环,用于对scripts列表中的每个Script对象进行警告处理。

  • if (!Objects.equals(script0.getTitle(), script0.getPaper().getTitle())) { ... }: 这行代码检查script0对象的标题是否与其试卷的标题相匹配,如果不匹配,则输出提示信息并返回。

  • 接下来的for循环用于遍历scripts列表中的每个Script对象,并输出每个问题的信息、答案和评分,以及整体得分。
    这是本应在控制类而实际在主类里的输入方法:
    input_queslist(Script script,String line):此方法用于解析包含问题信息的行,例如问题编号、文本和标准答案。它创建一个 Question 对象,设置其属性,并将其添加到 Script 对象中的问题列表中。

input_text(Script script,String line):该方法用于处理包含试卷结构信息的行。它根据提供的信息设置试卷的长度、标题和每个问题的分数。

input_script(Script script,String line,LinkedList Script scripts):此方法处理与试卷答卷或答题卡相关的输入。它解析行以提取有关答卷标题和答案的信息,根据答案是否正确设置每个答案的分数,并将答卷添加到答卷列表中。

warning(Script script):此方法通过将所有问题的分数相加来计算试卷的总分。如果总分不等于 100,则打印警告消息,指示试卷的满分不是 100 分。

type(String line):此方法根据行中特定的关键词(#N:、#T:、#S:)确定输入行的类型。它返回行的识别类型。

3.对于第三次题目集最后一道题的分析

“谋定而后动,不失其序。”

--这次在编写迭代三程序时终于学聪明了,在着手编写迭代三时,总结前面的教训我首先设计了一张UML图,其中包含我对迭代三题目需求的认识,以及构造相应的类去实现所需要的功能。
-- 而在UML设计之后,又恶补了迭代二没有实现的设计:深度拷贝。使用数据存储中介,以对象为单位去存储,在基本明白深度拷贝的原理并且使用简单的例子去验证之后,才将这类接口引用至迭代三当中。
-- 为应对 题目是否存在 题目是否被删除 题目是否无答案 我设计了题库类去分部实现此功能。并且对问题,答案这样的实体类增加了描述其状态的属性。以便判断情况给出相应的输出。


以下是对应的代码

点击查看代码
 有些方法确实没有必要给出,在查找这一块,为了方便查找链表中包含某个特定元素的的节点,专门构造了一些方法 check 去循环返回相应节点即Object,然而这样使代码显得冗长而缓慢。我应该使用Map(映射)去存储相关信息以便查找,借鉴使用了该容器类的代码,发现其实在比我的更为简短。

以下是对主方法的的分析:

ArrayListScript script_list = new ArrayList<>(); - 创建一个 ArrayList 对象 script_list,用于存储 Script 类型的对象。

ArrayList paper_list = new ArrayList<>(); - 创建一个 ArrayList 对象 paper_list,用于存储 Paper 类型的对象。

ArrayList student_list = new ArrayList<>(); - 创建一个 ArrayList 对象 student_list,用于存储 Student 类型的对象。

Depot depot0 = new Depot(); - 创建一个 Depot 类型的对象 depot0。

Script script0 = new Script(); - 创建一个 Script 类型的对象 script0。

Paper paper0 = new Paper(); - 创建一个 Paper 类型的对象 paper0。

Delete delete = new Delete(); - 创建一个 Delete 类型的对象 delete。

Student student0 = new Student(); - 创建一个 Student 类型的对象 student0。

String line_get = ""; - 创建一个空字符串 line_get,用于存储用户输入的一行文本。

Interface inter_face = new Interface(); - 创建一个 Interface 类型的对象 inter_face。

while (true) { - 进入一个无限循环,直到用户输入了 “end” 才退出循环。

line_get = scan.nextLine(); - 从标准输入流中读取用户输入的一行文本,并将其存储在 line_get 中。

if(!inter_face.check_format(line_get)){continue;} - 调用 inter_face 对象的 check_format 方法检查用户输入的文本格式是否符合要求,如果不符合则跳过本次循环继续等待用户输入。

if (Objects.equals(line_get, "end")) { break; } - 如果用户输入了 “end”,则退出循环,结束程序。

else if (line_get.contains("#N:")) { Controller.input_queslist(depot0, line_get);} - 如果用户输入的文本包含 “#N:”,则调用 Controller 类的 input_queslist 方法,将输入的内容解析并存储到 depot0 对象中。

else if (line_get.contains("#T:")) { Controller.input_text(paper0, line_get, depot0);...} - 如果用户输入的文本包含 “#T:”,则调用 Controller 类的 input_text 方法,将输入的内容解析并存储到 paper0 对象中,然后将 paper0 克隆一份加入 paper_list。

else if (line_get.contains("#S:")) { Controller.input_script(script0, line_get);...} - 如果用户输入的文本包含 “#S:”,则调用 Controller 类的 input_script 方法,将输入的内容解析并存储到 script0 对象中,然后将 script0 克隆一份加入 script_list。

else if (line_get.contains("#X:")) { Controller.input_X(student0, line_get, student_list); } - 如果用户输入的文本包含 “#X:”,则调用 Controller 类的 input_X 方法,将输入的内容解析并存储到 student0 对象中,并将其加入 student_list。

else if (line_get.contains("#D:")) { ...} - 如果用户输入的文本包含 “#D:”,则执行相应的操作,具体操作需要通过正则表达式解析得到命令,然后调用 delete 对象的相应方法进行处理。

for(Script s:script_list){...} - 遍历 script_list 中的每个 Script 对象,依次执行其中的评分操作和输出操作。
对新加的重要实体类,删除类的分析:

depot0.check_depot(this.x).setScores(0);

在题库depot0中查找标题为this.x的问题,并将其分数设置为0,表示此问题无效或已被删除。
depot0.check_depot(this.x).setQ_information("the question "+this.x+" invalid");

同样找到题库中的对应问题,设置问题信息为"the question "+this.x+" invalid",明确表明此问题已失效。
depot0.check_depot(this.x).setCondition(null);

将题库中对应问题的条件(condition)设为null,可能是移除与其相关的任何附加条件或限制。
随后是一个嵌套循环:

外层循环遍历了所有的试卷列表paper_list。
内层循环遍历每个试卷p中的问题列表p.getQuestion_list()。
if(q.getTitle()==this.x){

当发现某个问题的标题与要删除的问题标题this.x相同时,执行以下操作:
q.setCondition(null);

将当前试卷中对应问题的条件设为null。
q.setQ_information("the question "+this.x+" invalid");

设置当前试卷中对应问题的信息为已失效。
q.setStander_answer("");

踩坑心得

1.不考虑类的设计原则面向对象的设计原则:

使得类的职责多样,造成编写的困难,更为致命的是写程序时,想到哪写到哪,在没有能力掌控整个代码的情况下,这样做无疑会导致整个程序的崩盘。这是踩到的最大的坑

2.将类的对象之间进行赋值,造成关联影响

即改变原来对象的属性,被赋值的对象也受到影响,如同c语言指针一样,这个bug一度让我的迭代二瘫痪:以下是对相关问题的资料解答:

3.Java堆空间溢出:


这通常是因为程序尝试分配的内存超出了Java虚拟机堆的限制
后来了解到是过多的对象:如果程序创建了大量的对象,并且这些对象都存活在堆空间中,导致堆空间被耗尽。这里是对循环的边界没有设定好。

正则表达式的使用错误:


仅仅依照题目给的正确格式去分割,而不考虑数据输入的变化,“ ”的分割使得在答案内容前后有空格的情况下无法匹配,以这样的分割的字符数组去对每一个字符串匹配,造成输入错误。应对此类情况最好不要分割后再循环。
而是在正则表达式上下功夫,如()+在while循环里多次匹配而不破坏字符串的完整性

改进建议

对于三次迭代,其实改进的地方是共通的,最为主要的还是这篇blog反复提到的问题,面向对象程序中类的设计:
我应该对迭代一,迭代二,中的试卷类从中分离出一个题库类,当然,试卷类依旧含有问题类集合。对正则输入的方法单独放进一个Contraller类中,以及相关的匹配和上文提到的check方法。再构造一个接口类,应对输出,这样对于题目的迭代也会有较好的可塑性,提升可持续改进的能力。
将实体类集合,比如Linkedlist//<student//>//<question//>//<answer//>使用Map类型的集合,简化数据的调用,清晰代码的结构。

总结

一.学到了什么

这可有的说了,由于相关资料太多,只列举相对重要的。

  • Linkedlist,Arraylist容器类的相关概念及适用情况:
    Linkedlist和ArrayList都是Java中常用的容器类,用于存储和操作一组对象。
    ArrayList:
    ArrayList是基于数组实现的动态数组,可以根据需要自动增长容量。
    它提供了快速随机访问元素的能力,因为它通过索引直接访问数组元素。
    插入和删除操作在ArrayList中可能比较耗时,因为需要移动其他元素来保持连续性。
    ArrayList适用于需要频繁随机访问元素、不需要频繁插入和删除操作的场景。
    LinkedList:
    LinkedList是基于链表实现的,每个元素都包含一个指向前一个和后一个元素的引用。
    插入和删除操作在LinkedList中比较快速,因为只需要改变指针的指向。
    但是随机访问元素的性能比ArrayList差,因为需要从头或尾部开始遍历链表。
    LinkedList适用于需要频繁插入和删除操作、对随机访问要求不高的场景。
    总的来说,如果需要频繁随机访问元素,应该使用ArrayList;如果需要频繁插入和删除操作,应该使用LinkedList。
  • 重写Object类中的toString方法:
    定义新的字符串表示形式: 通过重写 toString() 方法,可以返回一个自定义的字符串,其中包含对象的各个属性的信息,或者是对象的状态摘要,这样可以更清晰地了解对象的内容。
    意义:提供友好的输出:方便调试:避免意外的输出:
    要重写 Object 类中的 toString() 方法,可以在自定义类中添加一个同名方法,并在其中返回希望展示的&#
    字符串。需要注意的是,重写时应该保持方法签名不变,即方法名为 toString(),返回类型为 String,而且
    应该尽量保持方法的高效性和一致性。
  • 是否每一个对象都具有自己独立的类的属性?
    是的, 每个对象都具有自己独立的类属性。在面向对象编程中,类定义了对象的属性和行为,而对象是类的实例化。当你创建一个类的实例(对象)时,该对象会拥有类中定义的所有属性,并且这些属性的值是独立于其他对象的。
    例如,如果你有一个名为Car的类,该类具有属性color、brand和model,那么当你创建两个Car对象时,每个对象都有自己独立的color、brand和model属性。修改一个对象的属性不会影响另一个对象的属性,因为它们是相互独立的。
    这种独立性使得对象可以在程序中被独立地操作和修改,而不会影响其他对象的状态。这是面向对象编程的一个重要概念,称为封装性,它确保了代码的模块化和可维护性。
  • Map(映射)在Java中是一种数据结构,它允许将键映射到值:
    简单来说,Map就是一组键值对的集合,每个键都是唯一的,而每个键都对应着一个值。
    在实际应用中,我们可以将Map想象成一个字典,其中键是单词,值是该单词的定义。通过这个比喻,我们可以更好地理解Map的作用:通过键(单词)快速查找值(定义)。
    Java中的Map接口有多种实现,每种实现都有其特点和适用场景。其中最常用的实现包括:
    HashMap:基于哈希表实现的Map,它提供了快速的插入、删除和查找操作。但是,HashMap不保证键值对的顺序。
    TreeMap:基于红黑树实现的有序Map,它可以按照键的自然顺序或者通过比较器进行排序。TreeMap保持了键值对的有序性。
    LinkedHashMap:继承自HashMap,它除了具备HashMap的特性外,还保持了插入顺序,即它可以按照元素插入的顺序遍历。
    Map的使用非常广泛,特别是在需要快速查找、存储键值对的情况下。例如,在处理大量数据时,可以使用Map来存储数据并根据键快速检索。另外,在许多Java库和框架中,Map也被广泛应用,用于传递参数、存储配置信息等场景。
  • 如何实现深拷贝:
    .深拷贝(Deep Copy)是在进行复制操作时,将被复制对象的所有内容都复制到新的对象中,包括对象的所有引用类型的成员对象。这意味着在深拷贝后,原始对象及其所有相关对象的副本都是相互独立的,对其中一个对象的修改不会影响到其他对象。
    使用clone()方法:在Java中,你可以通过实现Cloneable接口并重写clone()方法来实现对象的浅拷贝。然而,clone()方法默认执行的是浅拷贝,因此如果你需要深拷贝,就需要在clone()方法中进行递归复制.这里给出我使用的深拷贝代码:

二.进一步学习和研究

接口的使用,在用过之后发现这真是一个很好的工具帮助程序更为直接的达到要求。方法的重写,以及类的设计。以及简化代码,增加代码清晰度的能力。

三.改进建议

希望题目和测试点的文字说明能够更加清晰,有序。希望能延长在实验室学习的时间,每次到8点40就开始赶人实在是很无奈🤷

posted @ 2024-04-21 11:31  23201828殷泓翔  阅读(40)  评论(0)    收藏  举报