基于Java的在线测试系统的设计与实现
韩冰,李桂香
(浙江同济科技职业技术学院,浙江杭州311231)
摘要:在线测试系统的开发与实现,具有很重要的现实意义。教考分离以及考务工作的全自动化管理可以被实现,校园网的软硬件资源可以有效地被利用。针对实际需要,基于Java和MySQL的基础上设计的在线测试系统,能更好的为学校的教科研和管理服务,大规模考试可以得到实现和推广。
关键词:在线测试;教考分离;全自动化
中图分类号:TP311.1文献标识码:A文章编号:1672-0164(2021)02-0055-07
1引言
在线测试系统的产生是教育信息化的趋势。充分利用学校现有的计算机软、硬件和网络资源实现无纸化考试,与传统考试方式相比,在线测试渗入了更多的技术环节,对实现安全性的途径、方法也提出了更高的技术要求。
利用JDK开发工具,开发了一个在线测试系统。系统的主要功能是试题的录入、查询、浏览、修改、删除、出题、组卷、学生成绩的导出等。其最大的特点是方便和灵活。通过用户名和密码登录到系统的主界面,可以进行试题的录入、删除、修改和查询,也可以进行试卷生成。利用试题导入导出的功能,可以将固定格式的题目导入到题库中。管理员登陆到系统,可以对用户系统进行管理。
2在线测试系统的需求分析
需求分析所做的调查与分析是发生在系统设计之前以及设计、开发过程中针对用户的需求所展开的,是系统设计、系统完善和系统维护的重要依据%
在线测试系统应该实现下列功能:
从界面设计来说,主要包括:欢迎界面的设计、登陆界面的设计、主界面设计、
考试规则界面设计、考试界面设计。
从功能模块来说,主要包括:读取配置文件实现、
基金顶目:
2020年度浙江省水利厅科技计划项目,编号RC2033;省属高校基本科研业务费资助顶目,编号FRF20QN004加载用户功能实现、修改个人信息功能的实现、考试逻辑与代码查阅功能实现、加载试题功能的实现、组卷和展示部分的实现、上下题及定时器的实现、成绩的计算功能的实现。
3在线测试系统的系统设计
系统设计是在可行性研究及需求分析的基础上展开的一系列概要设计和顶层规划。根据需求分析而得出的报告或结果测。
3.1核心类的设计
(1)用户类一User.java
在当前包中新建User类,按照实际需求定义五个私有的成员变量,id、name、pwd、tel、email0依次表示:用户ID、姓名、密码电话号码、邮箱。
然后定义公有化的getter和setter方法获取和设置变量的值。用来获取用户对象的相关信息。
对于用户类一User.java的设计,本文在当前包中新建User类,按照实际需求定义五个私有的成员变量,迅、name、pwd、tel、email□依次表示:用户ID、姓名、密码、电话号码、邮箱。然后定义公有化的getter和setter方法获取和设置变量的值。用来获取用户对象的相关信息。
(2)题目类—Question.]ava手机登陆界面设计
在当前包中新建Question类,表示试题的实体类。Question类的一•个对象代表题库中的一•题。包括题干和四个选项及正确答案。然后,在service中,通过loadquestionO 方法把所有题目都导进来。
对于题目类一Question.java的设计,本文在当前包中新建Question类,表示试题的实体类。Question类的一个
通信与信息技术2021年第2期(总第250期)|55
2021/03|解决方案I Communication&Information Technology
对象代表题库中的一题。包括题干和四个选项及正确答案。然后,在service中,通过loadquestionO方法把所有题目都导进来。
(3)题目信息类一Questionlnfo.java
当前包中新建Questioninfo类。当用户组卷后,一个Questioninfo对象包含一个question对象、用户所做的答案、试题的序号。
对于题目信息类一Questionlnfo.java的设计,本文当前包中新建Questioninfo类。当用户组卷后,一■个Questioninfo 对象包含一个question对象、用户所做的答案、试题的序号。
(4)用户格式的设计
用户信息文件存在于一个中。使用纯文本的方式来存放用户信息,每个用户的信息占用一行,各个字段之间使用英文半角的冒号来进行分隔。本例中的用户信息中,每个用户的信息包含五个字段,分别是:用户ID,姓名,密码,手机号码和。
3.2相关信息格式设计
(1)用户格式的设计
用户信息文件存在于一个中。使用纯文本的方式来存放用户信息,每个用户的信息占用一行,各个字段之间使用英文半角的冒号来进行分隔。本例中的用户信息中,每个用户的信息包含五个字段,分别是:用户ID,姓名,密码,手机号码和。
其所包含属性具体如表1所示。
*1用户属性表
属性描述
ED用户的ID
name用户名
pwd密码
tel手机号码
etnl邮箱开的三项对应分别存放题目答案、分数和难度级别。其余部分为题干。
其所包含属性具体如表2所示。
表2题库属性表
属性描述
@answer正确答案的题号
Score分数
Level难度级别
其他题干
图1截取了corejava-txt中的部分内容,这一段信息代表一个题目。每一项所代表的信息如下面箭头指向所示。其中"@answer=l"为正确答案的题号为第一项。"score"为分数。"level**为难度级别。共1-10,十个难度级别。
正确答集跑题号分数难度级别
t t t
@answer=l,score=5,level=l
-下列属于不合法Java标识符的是〈)。
_mem
题干"12a
Ml2
・$12
图1题库信息图
33基本的数据结构设计
在任何一个系统中前端调用数据和更新数据都需要数据库支持,所以数据结构的设计是不可或缺的。只有数据结构的设计完整了,系统所要完成的功能才能够实现%
(1)用户存储数据结构
用户信息的存储是以哈希表形式存储的。在哈希结构的<key,value〉键值对中,key代表用户的ID。即:下图中的l-n,n个用户。
Value代表User对象。也就是,一个用户除ID外的其他属性。包括:用户名、密码、手机号、邮箱。貝卩:图2中
(2)题库信息格式的设计
题库是在线测试系统的基础。一个测试系统的题库应能容纳足够数量的题目,这些题目在题库中的组织、分类及其特征信息的确立与描述将直接影响系统的工作效率与效果。
题库中的题目都是按照测验目标精心筛选和校准的,每一题目除了试题本身的内容外,还要具有答案。为了能够进行计算机判卷,试题必须是标准化的,比如选择题、判断题、填空题等,且允许用户在试题库中添加、修改、删除试题。
在此在线测试系统中,试题是存储在的文本文件中:其中第1行以字符"@”开头,以英文逗号隔用户ID1
用户ID2
用户ID3
用户IDn
图2用户借息存储结构图
56
的User 对象1—User 对象n 。
(2)题库存储数据结构
题库信息的存储同样是以哈希表形式存储的。在哈希
结构的<key,value 〉键值对中,key 代表题库的难度等级 levelo 共1-10,十个难度等级即图3中的levell-levellOo
Value 代表由同一难度级别的所有题目,它们组成了
一个链表。同一难度等级下,有多少道题目,就会有多少 个节点,即图3中的levell 试题1-levell 试题n 。
4核心功能实现
4.1登陆主界面的设计
在线测试系统用户登陆界面设置"开始”"考试成 绩”"考试规则”"信息修改”等选项。
4.2新建保存信息的FrameTool.java 文件框架
前面设计了考试系统分成登陆主界面、考试界面考试
规则界面等多个界面。已经完成了考试系统登录主界面的 部分,如果依次使用这些界面,可以依次实例化它们,并 通过setVisible 。方法来设置它们的显示和隐藏。但是如果
要返回上一个界面,重新实例化一个窗体显然是不行的,
因为重新实例化的窗体不再是曾经使用过的那个界面。如 果要想返回上一个界面,就需要把实例化窗体的代码写在
窗体类的作用域之外,并且,同一个用户(考生)登录进 入系统之后,他所打开的每一个窗口都应该是和他有关 的,也就是说,这些窗体之间是互相关联的。
此时新建一个FrameTool 类,将每一个界面类的一个
实例化的对象作为该类的静态成员属性,这样,就相当于
在当前工程中定义了几个全局变量,就可以随时再次访问 这些窗体了,这样就能很方便地实现窗体之间的前进、返 回等操作了。
public  class  FrameTool  {
public  static  ExamFrameexamFrame;
public  static  MenuFramemenuFrame;public  static  XLoginFramexLoginFrame; public  static  RuleFrameruleFrame;
}
4.3定义考试规则按钮侦听
针对考试系统主界面进行对应修改,增加考试规则事
件侦听。代码如下所示。
MenuFrame.java 增加考试规则事件侦听。 rule.addActiotiListeiier(new  ActionListenerO  {
@Overtide
public  void  actionPerfbrmed(ActionEvent  e) {
tty  {
FrameTool.ruleFrame  = new  RuleFrameQ;
FrameTool.ruleFram,e.setVisible(true); FrameTooLmenuFrame.s etVisible(£alse);
} catch  (IOException  el) {
// TODO  Auto-generated  catch  block
el  .printStackTraceO ;
}
}
});
难度levell
难度level2
难度level3 难度levd.4
难度level5
难度levd.6
难度levd.7 难度level8 难度levd.9 难度 levell 0
图3题库信息存储结构图
-> Levell  试题]
讣―Levell 试题2
13^……Levell  试题n
-> Levd2 试题 1 十― Level2 试题2
"3^……
Level2 试题n
-> Level3 试题]
十―Level3试题2
....
Levd3试题n
A
-> Level4 试题] 十― Level4 试题2
"3^……Levd4试题n
->| Level5 试题1十— Leveb 试题2R ……
Leveb 试题n
-> Level6 试题] 十― Level6 试题2
"3^……
Levd6试题n
-
> Level7 试题1
十二Level7试题2
.....
Levd7试题n
-> Level8 试题]
十— Level8试题2
"3^.....Level8 试题n -> Level9 试题] 十― Level9 试题2"3^……
Levd9试题n ■> Levell0 试题1
十― Lev^O  试题2
讣― .......
LevdlO  试题ti
通信与信息技术2021年第2期(总第250期)| 57
2021/03|解决方案I Communication&Information Technology
4.4修改个人信息功能的实现
考生如果修改个人信息。必定在考试系统主界面中,定义事件侦听。当考生点击按钮后,由事件侦听触发,打开修改个人信息的界面,同时在该显示界面中包含修改按钮,返回按钮。
显示修改个人信息界面的同时,要隐藏系统主界面,就意味着要将考生登录系统后的信息,保存,隐藏不显示。当从修改个人信息返回时,复显考试系统主界面。
修改个人信息界面中可以自行修改的内容包括:姓名、密码、电话、邮箱。考号这个部分是不允许修改的。修改姓名的JLabel中,姓名的内容来源于登陆用户1。與User. getName。方祛。密码、电话、邮箱等JLabel代码类似。
4.5考试逻辑与代码查阅功能实现
从clientproperties中取来姓名、编号、考试时间等信息。涉及到组卷、倒计时等功能、examService接口被实例化。具体实现代码如下:
public Examlnfb start。{
if(finished){
throw new RuntimeExceptioii("考试已经结束'*);
}
〃组卷
creatPaperQ;
examInfo.setQuestionCount(paper.s ize。;
exatnInfb.TimeLimitO);
examInfb.setTitle(entityContextgefntleO);
examInfo.setUser(logifiUsei);
return examlnfb;
}
在这段代码中,分别经历了创建考卷、创建考试信息对象、初始化考试信息对象、返回考试信息对象几个过程。
4.6加载试题功能的实现
4.6.1试题文件corej 的结构
试题文件存放在文本文件中,该文件存放位置在工程根目录下。在Eclipse中可以直接打开文本文档查看内容,如果显示乱码,可以在文件图标的右键菜单中选择"Properties”打开文件的属性对话框,设置文件的字符编码(Text file encoding)为*"Other:”,在Other后面的文本框中输入"GBK”,即可正常显示中文字符。
试题文件中存放着试题库,每个题目在文件中使用6行存放:其中第1行以字符“@”开头,以英文逗号隔开的三个键值对分别存放题目答案、分数和难度级别,例如answer=l表示答案为四个中的第一个,score=5表示此题分值5分,level"表示难度级别为1级(共有10级);第2行是题目描述,即题干;第3、4、5、6行分别是四个备选答案。
4.6.2保存试题和考生答案的Questioninfo类
Question类中只存放了试题,若要将其展示在考试界面,则需要给每个题目分配一个题号,如第1题、第2题等。另外,还需要把考生回答问题的答案保存下来。在此,新建一个类Questioninfo,把题目类Question的一个对象作为其成员变量;然后定义一个int型的成员变量questionindex用来存放题号,要注意的是这个题号是和其对应的题目绑定的,一旦生成试卷之后,各题目的顺序不再发生变化;然后定义一个列表型变量userAnswers用来存放考生答题的答案,因为有多选题,答案可能不止一个,所以定义成列表型。
同时,为了实现该类的数据存储和传输,在定义类的时候实现了Serializable接口。代码如下所示:
package exam_test;
import java.io.Serializable;//导入支持包
import j a va.util.A rraylist;
import java.util.List;
public class Questionlnfb implements Serializable{
//定义Questionlnfo类并实现Serializable接口使其序列化private Question question;
//定义Question类型的变量,用于存放一道题目
private intquestionltidex;
〃定义整型变量存放题目在试卷中的题号(第X题)
private List<Integer>u ser A nswers二
new ArrayList<Iiiteger>0;
〃定义列表结构存放考生所回答的答案
public QuestionlnfbO{
〃定义无参的构造方法
}
public Questionlnfb(tntquestionlndex,Question question){〃定义有参的构造方法初始化对象
this.question=question;
this.questionlndex=questionlndex;
}
public Questionlnfo(intquestionlndex,
Question question,List<Integer>userAnswers){〃重载有参的构造方法初始化对象
superO;
this.question=question;
this.usetAnswers=userAnswers;
this.questionlndex=questionindex;
}
//实现Questionlnfb类成员属性的gettei和settet方法可自动生成,此处省略
58
〃重写Object类的toSttiiigO方法返回该题目的输出形态public String toStringO{
return(questionindex+1)+”+StringO;
}
}
4.7读取试题文件并转存到试题库M即结构
目前已经准备好了存放试题的类Question,存放试题、题号和答案的类Questioninfo,以及试题库在内存中的存储结构Map。接下来,在综合业务逻辑Service类中,增加一些代码,用输入流的方式来读取试题文件corejavNtxt,并将读取到的数据依次写入到一个个的Question对象中生成一道道的试题,再把这些试题按照难度为键,放到试题库Map的值链表中去,每一个"难度"和"此难度的试题链表”作为一个键值对存放在试题库Map中。以上功能通过定义不同的方法来实现。在Service类中增加读取试题文件并转存到试题库Map的代码,核心代码如下所示。
public dass Service{//定义业务逻辑实现类
//……前文中已实现的定义成员属性的代码省略……
public static Map<Integer,ArrayList<Question>> questionMap—
new HashMap<Integpr,AtrayList<Q uestioti>>0;
//定义_々HashMap集合来存放试题库,使用泛型指定存放的数据类型
public static ArrayList<Questionlnfo>paper二new ArrayList<Q uestionlnfb>0;
//定义一个ArrayLis瞎放试卷上的试题
//……前文中已实现的定义成员方法的代码省略……
public void loadQuestionsO{//定义加载试题库的方法MyProperties my=new MyPropertiesO;〃读取配置信息
try{〃异常处理
BufferedReader in二new BufferedReader(
new InputS t reamReader(
new FilelnputStream(
〃通过多层包装输入流将文件读取方式转换为按行读入
String stt;〃定义字符串型临时变量
while((str二in.readLineO)!二null){
〃定义循环直至文件结尾
if(str.equalsC")||stt.startsWith("#")){
〃判断是否为空行或注释行,是则跳转到下一行
continue;
}
Question question=patseQuestion(
〃调用生成试题的方法,参数为一行试题信息和接下来的五行信息
〃试题文件中接下来的五行分别是题干和四个选项
adLine(),in.readLine(),
addQuestion(question);〃调用方法把该题目加入到题目列表中去
}
}catch(Exception e){
//异常处理
e.printStackTraceO;
}
}
private void addQuestion(Question question){
〃定义方法实现把题目加入同难度的题目列表
ArrayIist<Question>questionLists二
<(LevelO);
〃定义题目列表变量指向试题库Map中该题目的难度所对应的题目列表
if(questionLists==null){
//判断列表是否存在,若不存在则读取题目难度,以难度为键新建题目列表
questionLists二new ArrayList<Q uestion>0;
questionMap.LevelQ,questionLists);
}
questionLists.add(question);//将题目加入列表}
private Question parseQuestion(String str,String tide,
String opl,String op2,String op3,String op4){〃定义生成试题的方法将读取到的试题文件中的六行字符串放入Question类的对象
StringO data二str.split(n[@,][a-z]+=n);
〃调用字符串的split方法使用正则表达式将字符串分割,得到几个值存放在数组中
Question q=new QuestionO;//定义Question类的变量
/*
*接下来调用QuesHon类的封装方法设置各成员变量的值
通信与信息技术2021年第2期(总第250期)|59

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。