用Java實現一個簡單的考試系統

用Java實現一個簡單的考試系統

需求分析

該考試系統可以實現的功能和系統要求應該包括:

  • 學生:登錄、考試、考試後查看成績
  • 老師:出題目(往題庫中添加新題目)、批閱卷子(同時打分)
  • 考試系統:學生的登錄校驗、存儲學生的賬號和密碼、存儲題庫、去除題庫中重複題目、隨機抽取一定數目的題目組合成一張卷子、卷子中題目的選項是隨機打亂的

設計思路

首先,最關鍵的是想好怎麼去存儲題目,題目由題幹、選項以及正確答案3部分組成。
數組、Collection集合,Map集合其實都能實現。

其中,我們很自然想到,使用HashMap去存儲可能比較簡單,它可以用key-value的形式讓題目和答案產生一一對應的關係,題幹存儲爲key,選項和答案存儲爲value,或者題乾和選項存儲爲key,答案存儲爲value。其實都行,都可以實現,問題是這樣設計真的合理嗎?

如果把題目存儲爲Map集合,看起來也許題目和答案的對應關係很強,但是實際上操作起來可能不是那麼方便。map集合的get方法,根據一個鍵找尋對應的值,這樣可讀性不是很好,而且map集合的遍歷,查找也不是那麼方便。

除了變量,數組,集合可以用來存儲數據,可不要忘了Java當中還有一個,那就是對象,這纔是我們首先要想到的。正所謂萬物皆對象,數據封裝在對象,可讀性也會大大增強。

我們不妨使用一個Question對象,來存儲單個的題目。把所有的題目存儲到HashSet集合裏,理由是HashSet集合可以自動幫我們自動去除重複元素,也就是去除重複的題目。

我們假設只要題幹相同,這兩個題目就是相同的題目。

爲了更方便地操作題目中題幹、選項、答案這3部分,我們把這3個內容當作屬性來處理。

但是可不要忘了想要讓HashSet幫我們去除重複元素,我們得先重寫hashCode()方法和equals()方法,也就是告訴HashSet,什麼樣的兩個元素可以認定是重複的。

然後,實現學生的登錄,系統裏需要存儲學生的信息,即賬號和密碼。賬號我們存儲爲String,密碼存儲爲Integer。賬號和密碼作爲鍵值對可以存儲在HashMap裏,這樣在登錄校驗的時候不用通過循環來判斷賬號是否存在以及密碼是否正確。

關於類和類之間的關係:學生和系統應該是依賴關係。即學生要考試,考試之前需要先登錄。登錄以後,纔可以進行考試。登錄的時候可以把考試系統作爲一個對象傳入學生的login方法裏,使學生和系統建立依賴關係。

老師和系統的關係,也必須是依賴關係,因爲老師可以往題庫中添加新的題目,而添加題目就要知道往哪個系統的題庫中添加。系統中的題庫(questionRepository),我們考慮設計爲static的,因爲它在整個系統中有且僅有一份。

編碼實現

Question類:

public class Question {
    private Integer orderNum;//題號
    private String title;//題幹
    private String[] option;//選項
    private String answer;//答案

    //不包含題號的構造方法,題號的後期生成卷子的時候賦予的
    public Question(String title, String[] option, String answer) {
        this.title = title;
        this.option = option;
        this.answer = answer;
    }
    @Override
    public boolean equals(Object obj){
        if (this==obj){
            //兩對象地址相同,那快別比了,肯定相同的對象
            return true;
        }
        if (obj instanceof Question){
            //首先造型
            Question anotherQue = (Question)obj;
            if(this.title.equals(anotherQue.title)){
                //題目的題幹相同,則認爲是同一道題,返回true
                return true;
            }
        }
        //obj不是Question類型的,那還比啥呀,肯定不相同
        return false;
    }
    @Override
    public int hashCode(){
        //返回題幹字符串的 hashcode
        return this.title.hashCode();
    }

    //get和set······

}

ExamMachine類:

public class ExamMachine {
    private static Map<String,Integer> userBox = new HashMap<>();//用戶信息
    private static Set<Question> questionRepository = new HashSet<>();//總的題庫

    public ExamMachine(){
        try {
            System.out.println("系統正在啓動,請稍等····");
            Thread.sleep(2000);
            System.out.println("正在加載題庫····");
            Thread.sleep(2000);
            System.out.println("系統初始化完畢,您可以登錄了");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    {
        userBox.put("admin",123);
        userBox.put("java",666);
    }
    {
        questionRepository.add(new Question("假設web應用的文檔根目錄爲MyApp,那麼可以從哪裏找到database.jar文件",new String[]{"MyApp目錄下","MyApp\\images目錄下","MyApp\\WEB-INF目錄下","MyApp\\WEB-INF\\lib目錄下"},"MyApp\\WEB-INF\\lib目錄下"));
        questionRepository.add(new Question("從以下哪一個選項中可以獲得Servlet的初始化參數",new String[]{"Servlet","ServletContext","ServletConfig","GenericServlet"},"ServletConfig"));
        questionRepository.add(new Question("哪一個對象可以用於獲得瀏覽器發送的請求。",new String[]{"HttpServletRequest","HttpServletResponse","HttpServlet","Http"},"HttpServletRequest"));
        questionRepository.add(new Question("運行jsp需要安裝_______Web服務器",new String[]{"Apache","tomcat","都要安裝","IIS"},"tomcat"));
        questionRepository.add(new Question("如何取得數據源",new String[]{"通過Http","通過ftp","JNDI","通過Connection對象"},"JNDI"));
        questionRepository.add(new Question("下列哪一個接口定義了用於查找、創建和刪除EJB實例",new String[]{"Home","Remote","Local","Message"},"Home"));
        questionRepository.add(new Question("下列哪個爲JSP的隱含對象",new String[]{"env","page","jspinfo","context"},"page"));
        questionRepository.add(new Question("下列哪一個是String類下的方法",new String[]{"add","put","compareTo","string"},"compareTo"));
        questionRepository.add(new Question("下列哪一個是java.util包下的類",new String[]{"Math","Object","Enum","Scanner"},"Scanner"));
        questionRepository.add(new Question("下列哪一個不是Java當中的包裝類",new String[]{"Integer","Char","Long","Short"},"Char"));
    }
    //學生的登錄校驗
    public boolean loginCheck(String name,Integer pwd){
        if(this.userBox.get(name)!=null && this.userBox.get(name).equals(pwd)){
            return true;
        }
        else return false;
    }
    //添加題目的方法按理說我們只讓老師使用,這裏就先不做權限控制了
    public boolean addQuestion(String title,String[] options,String answer){
        Question question = new Question(title,options,answer);
        return this.questionRepository.add(question);
    }
    //生成一張卷子,題目隨機
    public ArrayList<Question> generatePaper(){
        int num = 5;
        ArrayList<Question> paper = generatePaper(num);
        return paper;
    }
    //可以指定出幾道題目
    public ArrayList<Question> generatePaper(int num){
        System.out.println("系統正在隨機抽取題目生成試卷,這可能需要一段時間,請耐心等候···");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ArrayList<Question> questionRepository = new ArrayList<>(this.questionRepository);//把set集合暫時轉化爲list集合,爲的是利用索引來隨機
        HashSet<Question> paper = new HashSet<>();//用來存儲題目,set集合去重複
        //這個while循環用來生成不重複的一套題目
        while (paper.size()!=num){
            Question question = questionRepository.get(new Random().nextInt(questionRepository.size()));
            paper.add(question);
        }

        ArrayList<Question> newPaper = new ArrayList<>(paper);
        ArrayList<Question> finalPaper = new ArrayList<>();//空的ArrayList,用來存放有打亂順序的,有帶選項號的答案的,沒有題號的試題。
        //這個for循環用來給題目中的選項打亂順序
        for(int i = 0;i<paper.size();i++){
            Question question = newPaper.get(i);
            //選項隨機調換順序,每次生成的選項順序都不一樣
            //生成隨機不重複的索引號,用來生成順序隨機的選項
            int[] indexs = this.generateRandomIndexs(4, 4);
            String[] oldOptions = question.getOption();
            String[] newOptions = new String[oldOptions.length];
            char firstOrder = 'A';
            //設置選項的內容,選項的順序隨機
            for (int j = 0;j<oldOptions.length;j++){
                String optionOrder = String.valueOf((char)(firstOrder+j));//將char字符轉化爲String
                String option = oldOptions[indexs[j]];
                newOptions[j] = optionOrder+"."+option;
                if (option.equals(question.getAnswer())){
                    question.setAnswer(newOptions[j]);
                }
            }
            question.setOption(newOptions);
            finalPaper.add(question);
        }

        return finalPaper;
    }

    //將試卷打印到控制檯上,不是自己調用,等着學生的startExam方法裏面調用
   public String[] printPaper(ArrayList<Question> paper){
        Scanner input = new Scanner(System.in);
        String[] answers = new String[paper.size()];
        for (int i = 0;i<paper.size();i++){
            Question question = paper.get(i);
            question.setOrderNum(i+1);//設置題號
            System.out.println(question.getOrderNum()+"."+question.getTitle());

            String[] options = question.getOption();
            for (int j = 0;j<options.length;j++){
                System.out.println("\t"+options[j]);
            }
            System.out.println("請輸入您認爲的正確答案:");
            answers[i] = input.nextLine();//學生的選項
        }
       System.out.println("考試結束,請考生停止作答\n監考老師收取試卷中····");
       try {
           Thread.sleep(3000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return answers;
   }
   //==============================================================================================
    //生成亂序隨機索引號(難),生成隨機選項的時候要用它
    private int[] generateRandomIndexs(int num, int bound){
        //num生成幾個,bound是邊界
        int[] indexs = new int[num];
        for(int i = 0;i<indexs.length;i++){
            indexs[i] = -1;//初始化數組的每個元素爲-1
        }
        HashSet<Integer> set = new HashSet<>();//使用Hashset的原因是使用它的不重複規則控制while循環結束的條件
        int i = 0;//數組的索引號
        boolean isUse = false;//標記這個數組是否使用過
        while (set.size()<num){
            if (isUse==false){
                int randomIndex = new Random().nextInt(bound);
                indexs[0] = randomIndex;
                i++;
                set.add(randomIndex);
                isUse = true;
            }else {
                int random = new Random().nextInt(bound);
                boolean isAllNotSame = true;//是否全部不相同
                for (int j = 0;j<i;j++){
                    if (random == indexs[j]){
                        isAllNotSame = false;//只要有一個重複,就置爲false,不存
                    }
                }
                if (isAllNotSame){
                    //生成的索引與數組存儲的索引不相同,存儲它
                    indexs[i] = random;
                    i++;
                    set.add(random);
                }
            }
        }
        return indexs;
    }
}

Student類:

public class Student {
    private String name;
    private Integer password;
    private String examNum;//考號,沒有登錄系統則爲null

    public Student() {
    }

    public Student(String name, Integer password) {
        this.name = name;
        this.password = password;
    }
    //學生登錄
    public String login(ExamMachine em){
        boolean checkResult = em.loginCheck(this.name, this.password);
        if (checkResult){
            examNum = UUID.randomUUID().toString().replace("-","");//登錄成功,實例化考生考號
            System.out.println("登錄成功!\n請考生"+this.getName()+"憑藉考號"+examNum+"開始作答.\n不要作弊,一旦發現嚴肅處理!!!");
        }else {
            System.err.println("登錄失敗,用戶名或密碼錯誤");
        }
        return examNum;
    }
    //學生開始考試
    public String[] startExam(ExamMachine em,ArrayList<Question> paper){
        if (this.examNum.isEmpty()){
            System.err.println("您還沒有登錄,請您先登錄系統!");
            return null;
        }
        String[] answers = em.printPaper(paper);

        return answers;
    }
    //get以及set方法····

}

Teacher類:

public class Teacher {
    //往題庫裏添加新的題目
    public boolean addQuestion(ExamMachine em,String title,String[] options,String answer){
        Question question = new Question(title,options,answer);
        return em.addQuestion(title,options,answer);
    }
    //批閱卷子,打分數
    public float checkPaper(ArrayList<Question> paper,String[] answer){
        try {
            System.out.println("系統正在提交答案···");
            Thread.sleep(2000);
            System.out.println("老師們正在批閱,請耐心等候····");
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        float oneQueScore = 100F/paper.size();//一道題的分數
        float finalScore = 0;
        for (int i = 0;i<paper.size();i++){
            String trueAnswer = paper.get(i).getAnswer().substring(0,1);//把選項提取出來,charAt也可以
            if(trueAnswer.equalsIgnoreCase(answer[i])){
                finalScore+=oneQueScore;
            }
        }
        return finalScore;
    }
}

測試函數:

public class TestMain {
    public static void main(String[] args) {
        ExamMachine examMachine = new ExamMachine();
        Teacher teacher = new Teacher();
        Scanner input = new Scanner(System.in);
        System.out.println("請輸入用戶名");
        String name = input.nextLine();
        System.out.println("請輸入密碼");
        Integer password = input.nextInt();

        Student student = new Student(name,password);
        student.login(examMachine);

        ArrayList<Question> paper = examMachine.generatePaper(5);
        String[] answers = student.startExam(examMachine, paper);
        float score = teacher.checkPaper(paper, answers);
        System.out.println(student.getName()+"的最終成績爲:"+score+"分,(滿分爲100分)");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章