实验目的
熟练掌握图形界面程序设计。
实验内容
编写Java应用程序,实现“Java机考”的功能(“单机版Java简易机考程序”),包含单选题、多选题和判断题三种题型。
在主线程中创建一个Frame类型的窗口,在该窗口中再创建一个线程giveQuestion。
线程giveQuestion每隔20秒钟输出一个选择题(含A,B,C,D共4个选项,要求支持单选题和多选题,单选题用radio button,多选题用check box)或一个判断题(用radio button),选择题和判断题混合着给出;用户输入答案并按提交按钮提交结果(达到20秒自动提交结果);程序判断用户输入的答案是否正确(如果错选或漏选均得零分),并实时显示当前题目的正确答案、已经给出的题目的数量(分别给出单选题数量、多选题数量和判断题数量)、用户答对的数量(分别给出单选题数量、多选题数量和判断题数量)、用户的成绩和用户答题所花的总的时间。
如此循环15次,包括随机选择的5个单选题(每题1分)、随机选择的5个多选题(每题2分)和随机选择的5个判断题(每题1分),结束测试时给出最终成绩。
题库应至少包含10个单选题、10个多选题和10个判断题。要求使用图形用户界面。
在报告中附上程序截图、完整的运行结果截图和详细的文字说明。
实验过程
项目结构:
其中Questions类用于提供题目,QuizApp类使用图形化界面,实现了“Java机考”的功能。
Questions类代码如下:
package homework;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class Questions {
String title; //题目
String answer; //答案
String choose1; //选项1
String choose2; //选项2
String choose3; //选项3
String choose4; //选项4
int score; //题目分值
String type; //题目类型
public Questions(){
}
public Questions(String title, String answer, String choose1, String choose2, String choose3, String choose4,int score,String typeval) {
this.title = title;
this.answer = answer;
this.choose1 = choose1;
this.choose2 = choose2;
this.choose3 = choose3;
this.choose4 = choose4;
this.score = score;
this.type=typeval;
}
public Questions[] generate_question() {
// 单选题
List<Questions> singleChoiceQuestions = new ArrayList<>();
// 判断题
List<Questions> judgeQuestions = new ArrayList<>();
// 多选题
List<Questions> multipleChoiceQuestions = new ArrayList<>();
Questions[] questions = new Questions[30];
//单选题题库
questions[0] = new Questions("关于Java中类的说法,哪个是不正确的?", "C", "类体中只能有变量定义、常量定义和成员方法的定义,不能包含“x=3;”这样的语句。", "构造函数是类中的特殊方法", "主类一定要声明为public。", "一个java文件中可以有多个class定义。", 1, "single");
questions[1] = new Questions("下面哪个单词是Java语言的关键字?", "B", "Float", "this", "string", "unsigned", 1, "single");
questions[2] = new Questions("以下哪个是Java编程所必须的默认引用包?", "B", "java.sys包", "java.lang包", "java.net包", "以上都不是", 1, "single");
questions[3] = new Questions("下列哪个是Java的关键字?", "B", "NULL", "new", "instanceOf", "wend", 1, "single");
questions[4] = new Questions("下面哪个是Java语言中正确的标识符?", "C", "3com", "import", "that", "this", 1, "single");
questions[5] = new Questions("数组中可以包含什么类型的元素?", "D", "int型", "String型", "数组", "以上都可以", 1, "single");
questions[6] = new Questions("在Java中函数main()的返回值是?", "D", "String", "int", "char", "void", 1, "single");
questions[7] = new Questions("Java应用在消费电子市场上开发平台名称为:", "B", "JDK", "J2ME", "J2SE", "J2EE", 1, "single");
questions[8] = new Questions("下列哪个类声明是正确的?", "D", "abstract final class H1{}", "abstract private move(){}", "protected private number;", "public abstract class Car{}", 1, "single");
questions[9] = new Questions("构造方法何时被调用?", "B", "类定义时", "创建对象时", "调用对象方法时", "使用对象的变量时", 1, "single");
//判断题题库
questions[10] = new Questions("switch(exp)语句中,exp的数据类型不能是char", "F", "正确", "错误", "null", "null", 1, "Judge");
questions[11] = new Questions("int array[] = new int[3]{(1,2,3)};该语句语法错误。", "T", "正确", "错误", "null", "null", 1, "Judge");
questions[12] = new Questions("Boolean不是java的关键字。", "T", "正确", "错误", "null", "null", 1, "Judge");
questions[13] = new Questions("final abstract int f();方法声明正确。", "F", "正确", "错误", "null", "null", 1, "Judge");
questions[14] = new Questions("通过类名来访问静态成员变量和调用静态方法是一个好的编程习惯。", "T", "正确", "错误", "null", "null", 1, "Judge");
questions[15] = new Questions("Java中一个类可以继承多个类(extends 后面跟上多个类)。", "F", "正确", "错误", "null", "null", 1, "Judge");
questions[16] = new Questions("在Java程序的实际开发中,常量通常是不同对象间共享的静态变量,因此用final和static来修饰。", "T", "正确", "错误", "null", "null", 1, "Judge");
questions[17] = new Questions("创建子类的对象时,先调用子类自己的构造方法,然后调用父类的构造方法。", "F", "正确", "错误", "null", "null", 1, "Judge");
questions[18] = new Questions("不同操作系统(例如Windows和Linux)上安装的JVM是不一样的。", "T", "正确", "错误", "null", "null", 1, "Judge");
questions[19] = new Questions("上转型对象可以通过被子类重写的方法来间接地操作子类新增的成员变量。", "T", "正确", "错误", "null", "null", 1, "Judge");
//多选题题库
questions[20] = new Questions("重写说法正确的是?", "BC", "重写不能比父类方法权限修饰符更大", "重写可以比父类方法权限修饰符更大", "重写不能抛出比父类更大的异常", "重写可以抛出比父类更大的异常", 2, "multiple");
questions[21] = new Questions("重写说法不正确的?", "BD", "重写不是重载", "重写是重载", "重写是父子类", "重写需要一个类就可以", 2, "multiple");
questions[22] = new Questions("super关键字能做什么?", "ABC", "调用父类构造方法", "调用父类属性", "调用父类方法", "以上都不是", 2, "multiple");
questions[23] = new Questions("super关键字用法正确的是?", "ACD", "super()", "super().test()", "super.test", "super.test()", 2, "multiple");
questions[24] = new Questions("super关键字说法错误的是?", "ABD", "可以调用父类构造方法", "可以调用父类函数", "可以调用父类私有属性", "不能调用父类私有属性", 2, "multiple");
questions[25] = new Questions("super()说法错误的是?", "BCD", "可以调用父类构造方法", "可以调用父类函数", "可以调用父类私有属性", "可以调用父类静态属性", 2, "multiple");
questions[26] = new Questions("final关键字可以修饰?", "ABC", "常量", "类", "方法", "构造方法", 2, "multiple");
questions[27] = new Questions("以下说法错误的是?", "CD", "final修饰的类可以被继承", "final修饰的方法可以被重写", "final修饰的方法不能被继承", "final修饰的类不能被继承", 2, "multiple");
questions[28] = new Questions("以下说法正确的是?", "AB", "final可以修饰方法", "final可以修饰类", "dad", "final关键字不能修饰类", 2, "multiple");
questions[29] = new Questions("以下哪些关键字不能能和final共同使用?", "AB", "private", "abstract", "static", "以上都不是", 2, "multiple");
for(int i=0;i<10;i++){
singleChoiceQuestions.add(questions[i]);
}
for(int i=10;i<20;i++){
multipleChoiceQuestions.add(questions[i]);
}
for(int i=20;i<30;i++){
judgeQuestions.add(questions[i]);
}
// 从每种题型中各随机抽取5道题
List<Questions> selectedQuestions = new ArrayList<>();
selectedQuestions.addAll(selectRandomQuestions(singleChoiceQuestions, 5));
selectedQuestions.addAll(selectRandomQuestions(judgeQuestions, 5));
selectedQuestions.addAll(selectRandomQuestions(multipleChoiceQuestions, 5));
// 打乱题目顺序
Collections.shuffle(selectedQuestions);
// 将List转换为数组
return selectedQuestions.toArray(new Questions[0]);
}
private List<Questions> selectRandomQuestions(List<Questions> questionsList, int count) {
List<Questions> selectedQuestions = new ArrayList<>();
Random random = new Random();
// 随机抽取count道题目
while (selectedQuestions.size() < count) {
int index = random.nextInt(questionsList.size());
Questions selectedQuestion = questionsList.get(index);
// 避免重复选择同一道题
if (!selectedQuestions.contains(selectedQuestion)) {
selectedQuestions.add(selectedQuestion);
}
}
return selectedQuestions;
}
}
文字说明:
这段代码用于生成随机的考试题目。该类包含了题目的各项属性,包括题目内容、答案、选项、分值以及题目类型等。通过调用类的"generate_question()"方法,可以生成一组随机的题目数组。题目按照三种类型分别存储在单选题、判断题和多选题的题库中,每种类型的题目数量为10道。在生成题目时,从每个题库中随机选择5道题目,组成一个新的题目列表。最后,将题目顺序打乱后返回作为结果。
QuizApp类代码如下:
package homework;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
public class QuizApp extends JFrame {
// 各个成员变量
private JLabel timeAndScoreLabel; // 用于显示已用时间和总分的标签
private JLabel questionCountLabel; // 用于显示题目数量的标签
private JLabel correctCountLabel; // 用于显示答对数量的标签
private JLabel questionLabel; // 用于显示问题的标签
private JRadioButton[] options; // 选项的单选按钮数组
private JButton submitButton; // 提交按钮
private JButton resetButton; //重置按钮
private ButtonGroup buttonGroup = new ButtonGroup();
private ButtonGroup buttonGroup1 = new ButtonGroup();
private ButtonGroup buttonGroup2= new ButtonGroup();
private ButtonGroup buttonGroup3 = new ButtonGroup();
private ButtonGroup buttonGroup4 = new ButtonGroup();
private int totalTime; // 总用时
private int totalScore; // 总分
private int totalQuestions; // 总题数
private int singleChoiceCount; // 单选题数量
private int multipleChoiceCount; // 多选题数量
private int trueFalseCount; // 判断题数量
private int correctCount; // 答对的题目数量
private int correctSingleChoiceCount; // 答对的单选题数量
private int correctMultipleChoiceCount; // 答对的多选题数量
private int correctTrueFalseCount; // 答对的判断题数量
private Timer questionTimer; // 问题计时器
private int elapsedSeconds; // 当前题目经过的秒数
private Questions[] questions; //题目
//创建一个基于GUI的Java考试应用程序
//构造函数,用于创建GUI窗口,包含题目、选项和按钮,以及用于显示时间、分数、题目数量和答对数量的标签。用户可以选择答案,并通过"修改答案"按钮进行修改,然后使用"提交"按钮提交答案并更新页面显示。此外还有计时器用于记录答题时间,还有单独记录每道题的时间,单道题超过20秒则自动切换至下一题。
public QuizApp() {
setTitle("Java机考");
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(13, 1));
// 创建并初始化时间和分数标签
timeAndScoreLabel = new JLabel("已用时:0秒 | 当前题目已用时间:0秒 | 总分:0分");
add(timeAndScoreLabel);
// 创建并初始化题目数量标签
questionCountLabel = new JLabel("总题数:15 | 单选题:5 | 多选题:5 | 判断题:5");
add(questionCountLabel);
// 创建并初始化答对数量标签
correctCountLabel = new JLabel("答对:0 | 单选题:0 | 多选题:0 | 判断题:0");
add(correctCountLabel);
// 创建并初始化问题标签
questionLabel = new JLabel("问题");
questionLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
add(questionLabel);
// 创建包含4个选项的单选按钮
options = new JRadioButton[4];
for (int i = 0; i < 4; i++) {
options[i] = new JRadioButton();
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
panel.add(options[i]);
add(panel);
}
//创建按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
// 创建修改答案按钮
resetButton = new JButton("修改答案");
resetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
clearRadioButtonSelection();
}
});
buttonPanel.add(resetButton);
// 创建提交按钮
submitButton = new JButton("提交");
submitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
checkAnswer();
}
});
buttonPanel.add(submitButton);
// 添加空白面板,使按钮之间有间距,使得布局简洁
JPanel spacingPanel = new JPanel();
spacingPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
add(spacingPanel);
add(buttonPanel);
initializeQuiz(); // 初始化考试
displayQuestion(); // 显示题目
questionTimer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
elapsedSeconds++;
totalTime++;
updateTimeLabel();
if (elapsedSeconds >= 20) {
checkAnswer();
elapsedSeconds = 0;
}
}
});
questionTimer.setInitialDelay(0);
questionTimer.start();
}
//用于初始化界面的属性和获取题目
private void initializeQuiz() {
totalTime = 0;
totalScore = 0;
totalQuestions = 15;
singleChoiceCount = 5;
multipleChoiceCount = 5;
trueFalseCount = 5;
correctCount = 0;
correctSingleChoiceCount = 0;
correctMultipleChoiceCount = 0;
correctTrueFalseCount = 0;
elapsedSeconds = 0;
Questions questionObj = new Questions();
questions = questionObj.generate_question();
}
//根据不同的题型进行展示
private void displayQuestion() {
updateTimeLabel();
if (singleChoiceCount > 0 || multipleChoiceCount > 0 || trueFalseCount > 0) {
Questions currentQuestion = questions[15 - (singleChoiceCount + multipleChoiceCount + trueFalseCount)];
String typePrefix = "";
if (currentQuestion.type.equals("single")) {
typePrefix = "[单选题]";
setRadioButtonOptions(currentQuestion.choose1, currentQuestion.choose2, currentQuestion.choose3, currentQuestion.choose4,currentQuestion.type);
} else if (currentQuestion.type.equals("multiple")) {
typePrefix = "[多选题]";
setRadioButtonOptions(currentQuestion.choose1, currentQuestion.choose2, currentQuestion.choose3, currentQuestion.choose4,currentQuestion.type);
} else if (currentQuestion.type.equals("Judge")) {
typePrefix = "[判断题]";
setRadioButtonOptions(currentQuestion.choose1, currentQuestion.choose2, currentQuestion.choose3, currentQuestion.choose4,currentQuestion.type);
}
questionLabel.setText(typePrefix + currentQuestion.title);
}
}
//校对答案并更新显示
private void checkAnswer() {
Questions currentQuestion = questions[15 - (singleChoiceCount + multipleChoiceCount + trueFalseCount)];
if(currentQuestion.type.equals("single")){
singleChoiceCount--;
}else if(currentQuestion.type.equals("multiple")) {
multipleChoiceCount--;
}else {
trueFalseCount--;
}
if (currentQuestion.type.equals("single")) {
String selectedOption = "";
if (options[0].isSelected()) {
selectedOption = "A";
} else if (options[1].isSelected()) {
selectedOption = "B";
} else if (options[2].isSelected()) {
selectedOption = "C";
} else if (options[3].isSelected()) {
selectedOption = "D";
}
if (selectedOption.equals(currentQuestion.answer)) {
correctCount++;
correctSingleChoiceCount++;
totalScore += currentQuestion.score;
}
}
if (currentQuestion.type.equals("multiple")) {
boolean option0Selected = options[0].isSelected();
boolean option1Selected = options[1].isSelected();
boolean option2Selected = options[2].isSelected();
boolean option3Selected = options[3].isSelected();
String selectedAnswer = "";
if (option0Selected) selectedAnswer += "A";
if (option1Selected) selectedAnswer += "B";
if (option2Selected) selectedAnswer += "C";
if (option3Selected) selectedAnswer += "D";
if (selectedAnswer.equals(currentQuestion.answer)) {
correctCount++;
correctMultipleChoiceCount++;
totalScore += currentQuestion.score;
}
}
if (currentQuestion.type.equals("Judge")) {
if (options[0].isSelected() && currentQuestion.answer.equals("T")) {
correctCount++;
correctTrueFalseCount++;
totalScore+=currentQuestion.score;
}
if (options[1].isSelected() && currentQuestion.answer.equals("F")) {
correctCount++;
correctTrueFalseCount++;
totalScore+=currentQuestion.score;
}
}
//更新显示
elapsedSeconds = 0;
correctCountLabel.setText("答对:" + correctCount + " | 单选题:" + correctSingleChoiceCount +
" | 多选题:" + correctMultipleChoiceCount + " | 判断题:" + correctTrueFalseCount);
displayQuestion();
//当所有题目答完时显示结果
if (singleChoiceCount == 0 && multipleChoiceCount == 0 && trueFalseCount == 0) {
updateTimeLabel();
questionTimer.stop();
timeAndScoreLabel.setText("已用时:" + totalTime + "秒 | 当前题目已用时间:" + elapsedSeconds + "秒 | 总分:" + totalScore + "分");
// 展示答题结果
showResult();
}
}
//展示测试结果
private void showResult() {
// 创建用于显示成绩和用时的消息框
String message = "你的总分为:" + totalScore + "分\n" +
"答题时间为:" + totalTime + "秒";
JOptionPane.showMessageDialog(null, message, "测试结果", JOptionPane.INFORMATION_MESSAGE);
// 关闭窗口
dispose();
}
//设置选项
private void setRadioButtonOptions(String option1, String option2, String option3, String option4,String type) {
clearRadioButtonSelection();
options[0].setText(option1);
options[1].setText(option2);
if (option3.equals("null")) {
options[2].setVisible(false);
} else {
options[2].setText(option3);
options[2].setVisible(true);
}
if (option4.equals("null")) {
options[3].setVisible(false);
} else {
options[3].setText(option4);
options[3].setVisible(true);
}
if ((singleChoiceCount > 0 || trueFalseCount > 0)&&(type!="multiple")) {
buttonGroup.add(options[0]);
buttonGroup.add(options[1]);
buttonGroup.add(options[2]);
buttonGroup.add(options[3]);
} else {
// 多选题的处理
buttonGroup1.add(options[0]);
buttonGroup2.add(options[1]);
buttonGroup3.add(options[2]);
buttonGroup4.add(options[3]);
}
}
//清空上一题的选择,使得当前题目的选项都处于未选状态
private void clearRadioButtonSelection() {
buttonGroup.clearSelection();
buttonGroup1.clearSelection();
buttonGroup2.clearSelection();
buttonGroup3.clearSelection();
buttonGroup4.clearSelection();
}
//更新时间显示
private void updateTimeLabel() {
timeAndScoreLabel.setText("已用时:" + totalTime + "秒 | 当前题目已用时间:" + elapsedSeconds + "秒 | 总分:" + totalScore + "分");
}
//启动应用
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new QuizApp().setVisible(true);
}
});
}
}
实验结果
应用界面:
答题过程:
蓝色框内用于记录题目的答题总用时,
绿色框内用于记录当前题目的用时,如果超过20秒,则自动提交并显示下一题,
红色框内用于记录所答题目的总分
黑色框内用于记录答对的题目的数量
测试结果: