本来是不打算写这个课程作业的博客的,但是后续结队编程又需要和队友相互交流代码,而我当时写代码的时候也没有过多的注释,为了我的结队队友“、、”能够更轻松的(至少不会想捶我)完成课程任务,我还是准备写篇博客简单介绍一下课程项目。
PS:也附上“、、”的博客网站,也是关于这次的项目的
程序要求
程序架构
使用java实现程序,总有5个类,并使用数据库来查重
1.Main
程序运行的地方,主要处理逻辑,程序状态和用户交互之类的
2.User抽象类
代表出题者的一个抽象类,拥有登录(连接数据库),出题,检查题目是否重复的功能
3.Primary,JuniorHigh,High三个实例用户类
继承User,并实现出题的具体方法
4.mysql数据库
实现题目的查重功能和用户信息存储功能
下面将从Main类开始介绍整个程序
Main类
变量
private int state = LOGIN;//代表程序状态,初始状态为登录状态
private static final int LOGIN = 0;//定义宏LOGIN
private static final int LOGGED_IN = 1;//定义宏LOGGED_IN
private User user;//当前程序的使用用户
private static Scanner scanner = new Scanner(System.in);//实例化输入对象
main方法,程序入口
public static void main(String[] args) throws SQLException, ClassNotFoundException {
System.out.println("中小学数学卷子自动生成程序");
Main main = new Main();
while (true) {
//根据程序状态实现不同功能
switch (main.state) {
case LOGIN:
//实现登录功能
main.processLogin();
break;
case LOGGED_IN:
//实现登录后功能
main.processLoggedIn();
break;
}
}
}
processLogin()
private void processLogin() throws SQLException, ClassNotFoundException {
System.out.println("请输入用户名、密码");
String account = scanner.next();
String password = scanner.next();
//根据输入的账号密码获取用户实例对象
user = User.login(account, password);
if (user == null) {
System.out.println("请输入正确的用户名、密码");
} else {
System.out.println("当前选择为" + user.getDifficultyType() + "出题");
//将状态更新为已登录
state = LOGGED_IN;
}
}
processLoggedIn()
private void processLoggedIn() {
System.out.println("准备生成" + user.getDifficultyType() + "数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):");
//接收用户输入
String scannerContent = scanner.next();
//监测用户输入是否为int型数据
try {
int questionsNumber = Integer.parseInt(scannerContent);
//-1为退出登录,将状态重置为登录状态,并将用户对象置为null
if (questionsNumber == -1) {
state = LOGIN;
user = null;
} else if (questionsNumber >= 10 && questionsNumber <= 30) {
System.out.println("指令正确");
//开始生产题目,传入的参数为:用户的难度类型,题目数量
user.generateQuestionsByType(user.getDifficultyType(), questionsNumber);
} else {
System.out.println("请输入正确的指令");
}
}
//用户输入为非int型,监测是否为“切换为”指令
catch (Exception NumberFormatException) {
if (scannerContent.startsWith("切换为")) {
String type = scannerContent.substring(3);
//重置用户的难度类型
if (type.equals("小学") || type.equals("初中") || type.equals("高中")) {
user.setDifficultyType(type);
} else {
System.out.println("请输入正确的指令");
}
} else {
System.out.println("请输入正确的指令");
}
}
}
User类
变量
private String type;//用户类型
private String account;//用户账号
private String password;//用户密码
private String difficultyType;//难度类型
//下面的变量需要设置为自己的数据库数据
private static final String URL = "jdbc:mysql://localhost:3306/testgeneration?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";//数据库连接地址
private static final String USER = "root";//数据库用户名
private static final String PASSWORD = "1234";//数据库密码
构造器,getter,setter不再赘述
但是需要注意保护用户隐私,不比设置密码的getter和setter
login
public static User login(String account, String password) throws SQLException, ClassNotFoundException {
//1.加载驱动程序
Class.forName("com.mysql.cj.jdbc.Driver");
String searchedType;//从数据库获取到的类型
String sql = "SELECT identity from user where account=? and password=?";
//数据库的运行很耗费资源,所以需要及时关闭
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
//下面使用的是PreparedStatement,用法会贴在下面
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setObject(1, account);
preparedStatement.setObject(2, password);
try (ResultSet resultSet = preparedStatement.executeQuery()) {
if (resultSet.next()) {
//从数据库获取类型
searchedType = resultSet.getString("identity");
//返回实例对象
switch (searchedType) {
case "小学":
return new Primary(account, password, searchedType);
case "初中":
return new JuniorHigh(account, password, searchedType);
case "高中":
return new High(account, password, searchedType);
}
}
//查询失败,返回null
else {
return null;
}
}
}
}
return null;
}
statement 、prepareStatement的用法和解释,转自CSDN
getQuestionsArray
//由子类实现的抽象方法
public abstract String[] getQuestionsArray(int number);
generateQuestions
//根据输入的题目数量生成题目,并将其添加到数据库
public void generateQuestions(int number) {
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
try {
//测试重复问题,addQuestionsToDatabase会返回添加失败的个数,getQuestionsArray是根据不同类型的对象生成的,也就是不同难度的题目生成方法
int failNum = addQuestionsToDatabase(getQuestionsArray(number), timestamp);
while (failNum != 0) {
failNum = addQuestionsToDatabase(getQuestionsArray(failNum), timestamp);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
addQuestionsToDatabase
//返回添加失败的问题的个数
protected int addQuestionsToDatabase(String[] questions, Timestamp time) throws ClassNotFoundException, SQLException {
int count = 0;
//1.加载驱动程序
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "INSERT INTO testgeneration.questions (question, account, created_time) VALUES (?, ?, ?);";
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD)) {
try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
//创建文件对象
File file = new File("questionsData/" + account);
//如果没有用户的文件就创建文件夹
if (!file.exists()) {
file.mkdir();
}
// System.out.println(file.getPath());
//创建文件写入对象
try (FileWriter fileWriter = new FileWriter(file.getPath() + "/" + new SimpleDateFormat("yyyy年-MM月-dd日-HH时-mm分-ss秒").format(time) + ".txt", true)) {
//在多次写入时preparedStatement就能大大降低运行成本
for (int i = 0; i < questions.length; i++) {
preparedStatement.setObject(1, questions[i]);
preparedStatement.setObject(2, account);
preparedStatement.setObject(3, time);
try {
preparedStatement.executeUpdate();
fileWriter.write(questions[i] + "\r\n");
}
//如果重复则会报错,并将重复的题目数量+1
catch (SQLException e) {
System.out.println("'" + questions[i] + "'" + "与之前的题目重复,准备重新出题");
count++;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return count;
}
User子类
User的子类主要介绍生成题目的方法
Primary
//符号集
private static final String[] symbols = {"+", "-", "*", "/"};
@Override
public String[] getQuestionsArray(int number) {
String[] questions = new String[number];
//StringBuilder在操作变换的字符串的时候效率更高
StringBuilder question = new StringBuilder();
//以当前时间作为随机种子
Random random = new Random(System.currentTimeMillis());
for (int index = 0; index < number; index++) {
//随机生成操作数数量,最少为两个
int operandsNumber = random.nextInt(4) + 2;
//随机生成左括号的位置
int bracketStart = random.nextInt(operandsNumber);
//随机生成右括号的位置
int bracketEnd = random.nextInt(operandsNumber);
//判断括号位置是否合法
boolean isBracketIllegal = bracketStart < bracketEnd;
int[] operands = new int[operandsNumber];
for (int i = 0; i < operandsNumber; i++) {
//操作数为1~100
operands[i] = random.nextInt(100) + 1;
//括号位置合法,并且当前为左括号位置
if (isBracketIllegal && i == bracketStart) {
question.append("(");
}
question.append(operands[i]);
//操作数合法并且当前为右括号位置
if (isBracketIllegal && i == bracketEnd) {
question.append(")");
}
if (i < operandsNumber - 1) {
//随机从符号集里抽取一个符号
int symbolIndex = random.nextInt(symbols.length);
question.append(symbols[symbolIndex]);
}
//最后一个操作数后面为=号
else {
question.append("=");
}
}
questions[index] = question.toString();
System.out.println(question);
//重新设置问题
question.delete(0, question.length());
}
return questions;
}
JuniorHigh
//符号集
private static final String[] symbols = {"+", "-", "*", "/"};
@Override
public String[] getQuestionsArray(int number) {
String[] questions = new String[number];
StringBuilder question = new StringBuilder();
Random random = new Random(System.currentTimeMillis());
for (int index = 0; index < number; index++) {
//操作数数量,1到5个
int operandsNumber = random.nextInt(5) + 1;
//括号起始点
int bracketStart = random.nextInt(operandsNumber);
//括号终止点
int bracketEnd = random.nextInt(operandsNumber);
//初中符号个数
int juniorSymbolNum = operandsNumber == 1 ? 1 : random.nextInt(operandsNumber) + 1;
//根号个数
int RadicalNum = random.nextInt(juniorSymbolNum + 1);
//平方个数
int squareNum = juniorSymbolNum - RadicalNum;
//是否有括号
boolean isBracketIllegal = bracketStart < bracketEnd;
int[] operands = new int[operandsNumber];
for (int i = 0; i < operandsNumber; i++) {
//是否加根号
boolean addRadical = operandsNumber == 1 ? RadicalNum == 1 : random.nextBoolean();
//是否加平方
boolean addSquare = operandsNumber == 1 ? !addRadical : !addRadical && random.nextBoolean();
operands[i] = random.nextInt(100) + 1;
if (isBracketIllegal && i == bracketStart) {
//在括号外加根号
boolean outsideRadical = random.nextBoolean();
if (outsideRadical && RadicalNum > 0) {
question.append("√");
RadicalNum--;
}
question.append("(");
}
//普通根号
if (addRadical && RadicalNum > 0) {
question.append("√");
RadicalNum--;
}
question.append(operands[i]);
//普通平方
if (addSquare && squareNum > 0) {
question.append("²");
squareNum--;
}
if (isBracketIllegal && i == bracketEnd) {
boolean outsideSquare = random.nextBoolean();
question.append(")");
//括号外平方
if (outsideSquare && squareNum > 0) {
question.append("²");
squareNum--;
}
}
if (i < operandsNumber - 1) {
int symbolIndex = random.nextInt(symbols.length);
question.append(symbols[symbolIndex]);
}
//最后一个操作数后面为=号
else {
question.append("=");
}
}
questions[index] = question.toString();
System.out.println(question);
question.delete(0, question.length());
}
return questions;
}
High
//符号集
private static final String[] symbols = {"+", "-", "*", "/"};
private static final String[] highSymbols = {"sin", "cos", "tan"};
@Override
public String[] getQuestionsArray(int number) {
String[] questions = new String[number];
StringBuilder question = new StringBuilder();
Random random = new Random(System.currentTimeMillis());
for (int index = 0; index < number; index++) {
//操作数数量,1到5个
int operandsNumber = random.nextInt(5) + 1;
//括号起始点
int bracketStart = random.nextInt(operandsNumber);
//括号终止点
int bracketEnd = random.nextInt(operandsNumber);
//初中符号个数
int juniorSymbolNum = random.nextInt(operandsNumber) + 1;
//根号个数
int RadicalNum = random.nextInt(juniorSymbolNum + 1);
//平方个数
int squareNum = juniorSymbolNum - RadicalNum;
//高中符号个数
int highSymbolNum = operandsNumber == 1 ? 1 : random.nextInt(operandsNumber) + 1;
//是否有括号
boolean isBracketIllegal = bracketStart < bracketEnd;
int[] operands = new int[operandsNumber];
for (int i = 0; i < operandsNumber; i++) {
//是否加高中符号
boolean addHighSymbol = operandsNumber == 1 || random.nextBoolean();
//是否加根号
boolean addRadical = !addHighSymbol && random.nextBoolean();
//是否加平方
boolean addSquare = !addRadical && random.nextBoolean();
operands[i] = random.nextInt(100) + 1;
if (isBracketIllegal && i == bracketStart) {
//在括号外加根号
boolean outsideRadical = random.nextBoolean();
//在括号外加高中符号
boolean outsideHighSymbol = !outsideRadical && random.nextBoolean();
if (outsideRadical && RadicalNum > 0) {
question.append("√");
RadicalNum--;
}
if (outsideHighSymbol && highSymbolNum > 0) {
question.append(highSymbols[random.nextInt(highSymbols.length)]);
highSymbolNum--;
}
question.append("(");
}
//普通根号
if (addRadical && RadicalNum > 0) {
question.append("√");
RadicalNum--;
}
if (addHighSymbol && highSymbolNum > 0) {
question.append(highSymbols[random.nextInt(highSymbols.length)]);
highSymbolNum--;
}
question.append(operands[i]);
//普通平方
if (addSquare && squareNum > 0) {
question.append("²");
squareNum--;
}
if (isBracketIllegal && i == bracketEnd) {
boolean outsideSquare = random.nextBoolean();
question.append(")");
//括号外平方
if (outsideSquare && squareNum > 0) {
question.append("²");
squareNum--;
}
}
if (i < operandsNumber - 1) {
int symbolIndex = random.nextInt(symbols.length);
question.append(symbols[symbolIndex]);
}
//最后一个操作数后面为=号
else {
question.append("=");
}
}
questions[index] = question.toString();
System.out.println(question);
question.delete(0, question.length());
}
return questions;
}
数据库
user用户信息
create table user
(
account varchar(12) not null
primary key,
password varchar(15) null,
identity varchar(5) null
);
questions题目信息
将question和account设置为键,使添加时,同一个用户不能重发添加一个题,实现题目查重
create table questions
(
q_id int auto_increment
primary key,
question varchar(30) null,
account varchar(8) null,
created_time timestamp null,
constraint questions_pk
unique (question, account),
constraint questions_fk
foreign key (account) references user (account)
);
完整程序代码
项目完整文件已经上传到github,仅供参考
更多
欢迎来访我的个人网站