机考界面如下(单选题),上方是题目状态,下方是题目,1/5/1是已做题目数量、总共题目数量和答对题目数量。
再看一下多选题的界面。
判断题的界面。
回答正确时的反馈,会给出用时。
回答错误时的反馈,会显示正确答案和用时。
作答结束时会给出总成绩。
Question.java 题库
import java.util.ArrayList;
import java.util.List;
class SingleChoice {
public String question, optionA, optionB, optionC, optionD, correctAnswer;
public SingleChoice(String question, String optionA, String optionB, String optionC, String optionD, String correctAnswer) {
this.question = question;
this.optionA = optionA;
this.optionB = optionB;
this.optionC = optionC;
this.optionD = optionD;
this.correctAnswer = correctAnswer;
}
}
class MultipleChoice {
public String question, optionA, optionB, optionC, optionD, correctAnswer;
public MultipleChoice(String question, String optionA, String optionB, String optionC, String optionD, String correctAnswer) {
this.question = question;
this.optionA = optionA;
this.optionB = optionB;
this.optionC = optionC;
this.optionD = optionD;
this.correctAnswer = correctAnswer;
}
}
class TrueOrFalse {
public String question;
public boolean correctAnswer;
public TrueOrFalse(String question, boolean correctAnswer) {
this.question = question;
this.correctAnswer = correctAnswer;
}
}
public class Questions {
public List<SingleChoice> singleChoices = new ArrayList<>();
public List<MultipleChoice> multipleChoices = new ArrayList<>();
public List<TrueOrFalse> trueOrFalse = new ArrayList<>();
public Questions() {
// 添加单选题
singleChoices.add(new SingleChoice("Java中,下列哪个关键字用于声明一个类?", "A. int", "B. float", "C. class", "D. void", "C"));
singleChoices.add(new SingleChoice("什么是面向对象编程的基本特征之一?", "A. 继承", "B. 多态", "C. 封装", "D. 以上皆是", "D"));
singleChoices.add(new SingleChoice("在Java中,如何通过继承实现类之间的关系?", "A. extends关键字", "B. implements关键字", "C. super关键字", "D. this关键字", "A"));
singleChoices.add(new SingleChoice("以下哪个关键字用于在Java中创建接口?", "A. interface", "B. class", "C. abstract", "D. extends", "A"));
singleChoices.add(new SingleChoice("在Java中,下列哪个关键字用于控制程序的执行流程?", "A. if", "B. else", "C. switch", "D. for", "C"));
singleChoices.add(new SingleChoice("在Java中,下列哪个关键字用于声明一个常量?", "A. const", "B. final", "C. static", "D. var", "B"));
singleChoices.add(new SingleChoice("以下哪个不是Java的基本数据类型?", "A. int", "B. boolean", "C. double", "D. string", "D"));
singleChoices.add(new SingleChoice("在Java中,哪个关键字用于创建一个新的对象?", "A. new", "B. create", "C. make", "D. build", "A"));
singleChoices.add(new SingleChoice("以下哪个选项可以用于创建多线程?", "A. 哈希", "B. Thread类", "C. Main方法", "D. ArrayList类", "B"));
singleChoices.add(new SingleChoice("以下哪个关键字可以用于定义抽象类?", "A. abstract", "B. interface", "C. static", "D. final", "A"));
// 添加多选题
multipleChoices.add(new MultipleChoice("下列哪些选项是Java的基本数据类型?", "A. boolean", "B. string", "C. integer", "D. double", "AD"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的流控制语句?", "A. if-else", "B. for", "C. while", "D. function", "ABC"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的包装类?", "A. Integer", "B. Float", "C. Long", "D. Char", "ABC"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的集合类?", "A. ArrayList", "B. LinkedList", "C. HashSet", "D. Map", "ABCD"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的线程状态?", "A. NEW", "B. RUNNING", "C. WAITING", "D. DEAD", "ABCD"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的线程同步方法?", "A. wait()", "B. notify()", "C. synchronized()", "D. interrupt()", "ABC"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的异常类型?", "A. Checked Exception", "B. Unchecked Exception", "C. Error", "D. Throwable", "ABCD"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的注解类型?", "A. @Override", "B. @Deprecated", "C. @SuppressWarnings", "D. @Inject", "ABCD"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的IO流类型?", "A. FileInputStream", "B. InputStreamReader", "C. OutputStream", "D. Socket", "ABCD"));
multipleChoices.add(new MultipleChoice("下列哪些选项是Java中的网络通信协议?", "A. TCP", "B. UDP", "C. FTP", "D. HTTP", "ABCD"));
//添加判断题
trueOrFalse.add(new TrueOrFalse("Java是一种面向对象的编程语言", true));
trueOrFalse.add(new TrueOrFalse("Java中的final关键字用于标识一个类不可被继承", true));
trueOrFalse.add(new TrueOrFalse("Java中的String是基本数据类型", false));
trueOrFalse.add(new TrueOrFalse("Java中的数组长度可以动态改变", false));
trueOrFalse.add(new TrueOrFalse("Java中的interface可以实现多重继承", true));
trueOrFalse.add(new TrueOrFalse("Java中的double类型比float类型精度更高", true));
trueOrFalse.add(new TrueOrFalse("Java中的HashMap是线程安全的", false));
trueOrFalse.add(new TrueOrFalse("Java中的try块必须和catch或finally块配合使用", true));
trueOrFalse.add(new TrueOrFalse("Java中的抽象类可以有构造方法", true));
trueOrFalse.add(new TrueOrFalse("Java中的静态方法可以被子类重写", false));
}
}
JavaExam.java 主界面
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.util.Collections;
import java.util.List;
public class JavaExam extends JFrame {
private final JLabel labelQuestion;
private final JRadioButton radioButtonA, radioButtonB, radioButtonC, radioButtonD;
private final JCheckBox checkBoxA, checkBoxB, checkBoxC, checkBoxD;
private final ButtonGroup buttonGroup;
private final JButton buttonSubmit;
private final JLabel labelSingleChoiceNum, labelMultipleChoiceNum, labelTrueOrFalseNum, labelScore, labelTime;
private int singleChoiceCount = 0, multipleChoiceCount = 0, trueOrFalseCount = 0;
private int singleChoiceCorrectCount = 0, multipleChoiceCorrectCount = 0, trueOrFalseCorrectCount = 0;
private int score = 0, seconds = 0, questionIndex = 0;
private boolean isSingleChoice, isMultipleChoice, isTrueOrFalse, submit = false;
private final List<SingleChoice> singleChoices;
private final List<MultipleChoice> multipleChoices;
private final List<TrueOrFalse> trueOrFalse;
private final Timer timer;
private final JPanel panelQuestion = new JPanel();
class GiveQuestion implements Runnable {
@Override
public void run() {
Collections.shuffle(singleChoices);
Collections.shuffle(multipleChoices);
Collections.shuffle(trueOrFalse);
while (questionIndex < 15) {
submit = false;
if (questionIndex % 3 == 0) {
isSingleChoice = true;
showSingleChoiceQuestion(singleChoices.get(singleChoiceCount));
singleChoiceCount++;
} else if (questionIndex % 3 == 1) {
isMultipleChoice = true;
showMultipleChoiceQuestion(multipleChoices.get(multipleChoiceCount));
multipleChoiceCount++;
} else {
isTrueOrFalse = true;
showTrueOrFalseQuestion(trueOrFalse.get(trueOrFalseCount));
trueOrFalseCount++;
}
timer.start();
while (!submit) {
}
questionIndex++;
}
endExam();
}
}
public JavaExam() {
super("Java机考 By YeMaolin");
singleChoices = new Questions().singleChoices;
multipleChoices = new Questions().multipleChoices;
trueOrFalse = new Questions().trueOrFalse;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 250);
setLocationRelativeTo(null);
setLayout(new BorderLayout());
panelQuestion.setLayout(new GridLayout(5,1));
panelQuestion.setBorder(new EmptyBorder(0,15,0,0)); // 设置组件的外边距
labelQuestion = new JLabel("", SwingConstants.CENTER);
radioButtonA = new JRadioButton("A");
radioButtonB = new JRadioButton("B");
radioButtonC = new JRadioButton("C");
radioButtonD = new JRadioButton("D");
checkBoxA = new JCheckBox("A");
checkBoxB = new JCheckBox("B");
checkBoxC = new JCheckBox("C");
checkBoxD = new JCheckBox("D");
buttonGroup = new ButtonGroup();
buttonGroup.add(radioButtonA);
buttonGroup.add(radioButtonB);
buttonGroup.add(radioButtonC);
buttonGroup.add(radioButtonD);
JPanel panelInfo = new JPanel();
labelSingleChoiceNum = new JLabel("单选题:0/5/0", SwingConstants.CENTER);
labelMultipleChoiceNum = new JLabel("多选题:0/5/0", SwingConstants.CENTER);
labelTrueOrFalseNum = new JLabel("判断题:0/5/0", SwingConstants.CENTER);
labelScore = new JLabel("成绩:0分", SwingConstants.CENTER);
labelTime = new JLabel("用时:0秒", SwingConstants.CENTER);
panelInfo.add(labelSingleChoiceNum);
panelInfo.add(labelMultipleChoiceNum);
panelInfo.add(labelTrueOrFalseNum);
panelInfo.add(labelScore);
panelInfo.add(labelTime);
add(panelInfo,BorderLayout.NORTH);
add(panelQuestion,BorderLayout.WEST);
setVisible(true);
buttonSubmit = new JButton("提交");
add(buttonSubmit,BorderLayout.SOUTH);
buttonSubmit.addActionListener(e -> submitAnswer());
timer = new Timer(1000, e -> {
seconds++;
labelTime.setText("用时:" + seconds + "秒");
if (seconds >= 20) {
submitAnswer();
}
});
Thread giveQuestion = new Thread(new GiveQuestion());
giveQuestion.start();
}
private void endExam() {
timer.stop();
JOptionPane.showMessageDialog(this, "考试结束,您的成绩是" + score + "分!");
System.exit(0);
}
private void showSingleChoiceQuestion(SingleChoice singleChoice) {
panelQuestion.removeAll();
labelQuestion.setText(questionIndex + 1 + "/15[单选题]" + singleChoice.question);
radioButtonA.setText(singleChoice.optionA);
radioButtonB.setText(singleChoice.optionB);
radioButtonC.setText(singleChoice.optionC);
radioButtonD.setText(singleChoice.optionD);
panelQuestion.add(labelQuestion);
panelQuestion.add(radioButtonA);
panelQuestion.add(radioButtonB);
panelQuestion.add(radioButtonC);
panelQuestion.add(radioButtonD);
}
private void showMultipleChoiceQuestion(MultipleChoice multipleChoice) {
panelQuestion.removeAll();
labelQuestion.setText(questionIndex + 1 + "/15[多选题]" + multipleChoice.question);
checkBoxA.setText(multipleChoice.optionA);
checkBoxB.setText(multipleChoice.optionB);
checkBoxC.setText(multipleChoice.optionC);
checkBoxD.setText(multipleChoice.optionD);
panelQuestion.add(labelQuestion);
panelQuestion.add(checkBoxA);
panelQuestion.add(checkBoxB);
panelQuestion.add(checkBoxC);
panelQuestion.add(checkBoxD);
}
private void showTrueOrFalseQuestion(TrueOrFalse trueOrFalse) {
panelQuestion.removeAll();
labelQuestion.setText(questionIndex + 1 + "/15[判断题]" + trueOrFalse.question);
radioButtonA.setText("正确");
radioButtonB.setText("错误");
panelQuestion.add(labelQuestion);
panelQuestion.add(radioButtonA);
panelQuestion.add(radioButtonB);
}
private void submitAnswer() {
timer.stop();
String correctAnswer = "";
boolean right = false;
if (isSingleChoice) {
String userAnswer = "";
correctAnswer = singleChoices.get(singleChoiceCount - 1).correctAnswer;
if (radioButtonA.isSelected()) {
userAnswer = "A";
} else if (radioButtonB.isSelected()) {
userAnswer = "B";
} else if (radioButtonC.isSelected()) {
userAnswer = "C";
} else if(radioButtonD.isSelected()) {
userAnswer = "D";
}
if (userAnswer.equals(correctAnswer)) {
singleChoiceCorrectCount++;
score++;
right = true;
}
}else if (isMultipleChoice) {
correctAnswer = multipleChoices.get(multipleChoiceCount - 1).correctAnswer;
String userAnswer = "";
if (checkBoxA.isSelected()) {
userAnswer += "A";
}
if (checkBoxB.isSelected()) {
userAnswer += "B";
}
if (checkBoxC.isSelected()) {
userAnswer += "C";
}
if (checkBoxD.isSelected()) {
userAnswer += "D";
}
if (correctAnswer.equalsIgnoreCase(userAnswer)) {
multipleChoiceCorrectCount++;
score += 2;
right = true;
}
}else if (isTrueOrFalse) {
boolean correct = trueOrFalse.get(trueOrFalseCount - 1).correctAnswer;
correctAnswer = String.valueOf(correct);
if (radioButtonA.isSelected()&&correct||radioButtonB.isSelected()&&!correct) {
trueOrFalseCorrectCount++;
score++;
right = true;
}
}
if (right)
JOptionPane.showMessageDialog(this, "回答正确,用时" + seconds + "秒");
else JOptionPane.showMessageDialog(this, "回答错误,正确答案是 " + correctAnswer + "\n用时" + seconds + "秒");
update();
submit = true;
}
private void update() {
labelScore.setText("成绩:" + score + "分");
labelSingleChoiceNum.setText("单选题:" + singleChoiceCount + "/5/" + singleChoiceCorrectCount);
labelMultipleChoiceNum.setText("多选题:" + multipleChoiceCount + "/5/" + multipleChoiceCorrectCount);
labelTrueOrFalseNum.setText("判断题:" + trueOrFalseCount + "/5/" + trueOrFalseCorrectCount);
buttonGroup.clearSelection();
checkBoxA.setSelected(false);
checkBoxB.setSelected(false);
checkBoxC.setSelected(false);
checkBoxD.setSelected(false);
radioButtonA.setSelected(false);
radioButtonB.setSelected(false);
radioButtonC.setSelected(false);
radioButtonD.setSelected(false);
seconds = 0;
isSingleChoice = isMultipleChoice = isTrueOrFalse = false;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(JavaExam::new);
}
}
首先最主要的功能就是展示题目,我们先设计出三种题目类型的结构,然后建立起题库。首先是单选题,定义一个单选题类,题目内容和四个选项以及正确答案都用字符串来表示,并为之设置一个带参构造函数。
类似的定义一个多选题类,多选题和单选题唯一的区别在于多选题的正确答案不止一个,但我们都可以通过一个字符串来表示正确答案,因此这两个类实现是一模一样的。
对于判断题来说,只需要一个字符串记录题目内容,一个布尔变量记录正确的判断结果就行。
然后我们开始准备题库,定义一个题库类,建立起三种题型的容器。
然后在构造函数中添加题目。
添加10道单选题。
添加10道多选题。
添加10道判断题。
接下来是如何将题目展示在GUI界面上以及实现其他的功能逻辑的问题了。
定义一个类Java Exam,继承自JFrame,这样Java Exam就是一个窗口了,我们在主函数中使用SwingUtilities类中的invokeLater方法来在事件调度线程上执行Java Exam的构造函数。
在Java Exam构造函数中,我们定义窗口的一些基本属性,并从题库中取出三种类型的题目。
然后初始化选项组件,这里单选题和判断题可以共用一套radio Button,然后多选题用check Box。
将四个按钮加入到按钮组中,确保在同一个按钮组中只有一个单选按钮可以被选中,当用户选中其中一个单选按钮时,其他的单选按钮会自动变为未选中状态,实现单选功能。
实时显示三种题型的已做数目\总数目\答对数目和当前总成绩和用时,这些信息都放在一个JPanel容器里面。
设置提交按钮,实现一个事件监听器,当按钮被点击的时候调用提交答案方法,再设置一个定时器,如果作答超过20秒将自动提交答案完成作答。
最后创建一个线程giveQuestion用来展示题目。
来看giveQuestion的实现,我们先将题目的顺序打乱,这样每次重新启动程序题目的出现顺序都是随机的。
然后每种题型取前五道题一共十五道题进行展示,分别调用对应的展示函数进行题目展示。
然后开始计时,等待作答提交后切换下一题,如果没有在20秒内提交答案,系统也会自动调用提交函数进行提交然后自动切换下一题。当所有题目作答完毕后调用结束函数展示成绩。
对于三种题型的展示,因为涉及到题目之间的切换,所以我们使用了一个JPanel容器来装题目的组件,然后每次展示题目之前都将这个容器上面的所有组件清除,再添加上本题目的组件。
展示单选题,这里加上了题号。
类似的展示多选题。
展示判断题用的是和单选题同一套组件,但是判断题只需要用到两个radio Button即可表示正确与错误。
然后看提交答案的函数,这里首先让计数器停止计时,然后分题目类型核对答案。
核对单选题答案,先从题库中取出正确答案,然后取作答答案进行比较,答对加法,这里需要注意的是,必须对四个选项进行判断有没有选择,而不能最后一个用else情况处理,因为如果没有作答那么答案就应该为空,而不是else的选D。
核对多选题答案,这里需要按顺序将选择到的ABCD拼接起来作为一个字符串,然后直接和正确答案比较是否相等即可。
核对判断题答案,直接判断有没有选对即可。
然后给回作答反馈,如果回答正确,给出正确提示和用时,回答错误的时候给出正确答案和用时,再调用更新函数进行状态更新。
再来看更新函数,即更新成绩,三种题型的已做题目数量和答对题目数量的更新,还有选项状态的清除。
最后作答完毕结束时调用的结束函数用来停止计时和展示成绩。