摘要

这算是软件创新这笨比课程的第一个项目,也算是我自己一个人用java写的第一个东西(?).虽然时间总计没写多久,但还是再次让我体会到编程这东西就是得一边做,一边学。


题目要求以及大致编写思路

题目的目的是做一个覆盖小学,初中,高中的数学计算题出题器,并能够做到不重复和文件保存。具体要求在后面分析代码时给出。

实现起来也不是很难,先通过while循环进行不断检测输入,登录通过以后,再进行相关操作检测,然后跳转到对应操作语句。代码主体为一个main主类,然后写了4个函数接口:file,mysql,question和user。


逻辑流程

登录

题目要求

登录要求

用户表

也就是实现一个不同用户登录,并识别出其不同身份

具体代码及分析(user接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args){
int school = -1;//学校状态变量,同时以-1标记为未登录状态

//未退出程序时循环进行登录操作
while(true) {
Scanner sc = new Scanner(System.in);
String name = sc.next(); //输入用户名
String pass = sc.next(); //输入密码

if(school==-1) {
school = user.login(name, pass);//登录检测

//当输入正确后,school值更改为对应学校,并进入登录后操作
if(school!=-1) {
question.promot_choose(school);
school=user.loginned(school,name);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int login(String user, String pass) {//登录验证

String[] name={"张三1","张三2","张三3","李四1","李四2","李四3","王五1","王五2","王五3"};

//循环遍历数组,查看是否输入正确
for(int i=0;i<9;i++){
if(user.equals(name[i]) && pass.equals("123")){
//根据用户名在数组中对应位置判断对应学校,0为小学,1为初中,2为高中
return i/3;
}
}
System.out.println("请输入正确的用户名、密码");
return -1;//如果未检测到对应账号,返回-1
}

其实java嘛,应该以类/对象为主体,但就这个题而言,我个人觉得好像没啥必要(?),所以我全部都是用的接口实现方法,除主类外一个没有。

然后就登录而言,在主类里利用一个while循环实现重复的登录判定.利用一个school变量标记用户对应的学校(这些就应该搞个用户类)。然后通过user接口里的login方法,进行登录验证,并将获得的学校传给school,然后传入user接口的loginned方法进行登录后操作。

然后对于login方法,利用数组保存了9个用户名,并依据学校将其排序,小学为前三个,初中为中间三个,高中为后面三个。密码则都是123,就不需要另外储存。然后进行字符串匹配,未成功返回-1,说明登录失败,成功则返回用户名的数组对应位置以此标记用户对应学校。


登录后命令判断

具体要求

登录后命令操作

具体来说就是在登录后根据输入字符串实现对应操作。

具体代码及分析(user接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static int loginned(int school,String user){
//登录后,先创建用户对应table
mysql.create(user);
Scanner sc = new Scanner(System.in);
//当未退出时循环输入
while(school!=-1) {
question.promot_generete(school);//生成题目前提示
String execute = sc.next(); //输入操作字符串

try(){
int num = Integer.parseInt(execute);
if (num >= 10 && num <= 30) {question.generat()}
else if (num == -1) {school = -1;}
else {System.out.println("题目数量超出范围,请重新输入");}
}

catch{
if(execute.length()>=4) {
String inter=execute.substring(0,3);
//根据输入类型,进行对应转换,如果输入非法,则进行相关提示
if(inter.equals("切换为")){
if (execute.equals("切换为小学")) {school = 0;}
else if (execute.equals("切换为初中")) {school = 1;}
else if (execute.equals("切换为高中")) {school = 2;}
else System.out.println("请输入小学、初中和高中三个选项中的一个");
}else {···}
}else{···}

}
return -1;
}
}

成功登录后,就是对应操作。首先便是一个mysql的表单创建,然后输入对应操作语句。

首先利用try-catch语句分析该字符串是否能够转为int数。若能成功转换则说明是出题语句或者退出登录语句,然后判读其数值。

若为10-30,则通过question接口中的generate函数执行出题操作。

若为-1,则将school变量置为-1执行退出操作,其他的则输出提示语句提示输入数字偏离对应范围,需要重新输入。

若无法转换,此时转换函数就会报错,然后执行catch内语句。

先判断是否为转换语句,若是则根据对应改变更改school值即可,若不是,直接输出提示“输入非法”即可。


相关接口及函数

user接口里的函数基本在逻辑流程里解释过了,这里就主要解释mysql,file和question接口及其函数。

mysql.create

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void create(String user){
Connection connection = null;
Statement statement = null;
try {
// 注册数据库驱动
// 创建数据库连接对象
······
statement = connection.createStatement();
// 创建对应table,主键为question,确保题目不重复
String sql = "create table " + user + " ( question varchar(20) primary key )";
statement.executeUpdate(sql);
}catch (Exception e) {
e.printStackTrace();
} finally {
// 数据库释放资源
···
}
}

本函数传入一个user字符串,然后根据此字符串创建对应名称的题目表,表的主键为题目,利用主键不可重复的规则,达到题目不重复的目的。


mysql.save

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//题目的查重保存方法
//在数据库中,每个用户存在自己的一张表,题目对应为question为该table主键,当每次执行insert操作时可自动执行查重功能
static boolean save(String question, String user){//
Connection connection = null;
Statement statement = null;
boolean flag=true;

try {
// 注册数据库驱动
// 创建数据库连接对象
···

statement = connection.createStatement();
// 创建并执行相关的数据库插入语句,利用result获得插入结果
String sql = "insert into "+ user+ " value (\""+question+"\")";
int result = statement.executeUpdate(sql);

//执行语句后若返回值大于0,说明成功插入,没有重复,则返回true;反正返回false
if (result > 0) {
System.out.println("题目未重复");
flag=true;//
} else {
System.out.println("题目已重复");
flag=false;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 数据库释放资源
···
}
return flag;
}

save函数主要目的为生成题目后将其保存到数据库中。函数需要传入两个字符串,一个是题目字符串,一个是用户名字符串。

当调用函数后,根据用户名将题目字符串插入到对应表,如果能够成功插入,则说明没有重复,statement.executeUpdate函数就会返回一个大于0的int值,表示受到影响的表内行数,此时就可以将flag置为true,save函数就返回true值,表示题目没有重复,成功保存到数据库中;反之函数就会返回0表示没有行受到影响也就是没有成功插入。此时将flag置为false,表示题目重复。


file.generate

题目要求

文件生成要求

具体代码及分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static File generate(String user){
Date date = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String time=formatter.format(date);
System.out.println(time);//获取当前时间

//文件夹不存在时生成对应文件夹
String Path = "···" + user;
File folder = new File(Path);
if (!folder.exists()) folder.mkdirs();

//生成对应text文件
File x = null;
try {
x = new File(Path,time+".txt");
if (x.createNewFile()) {
System.out.println("文件已生成: " + x.getName());
}
} catch (IOException e) {
e.printStackTrace();
}

//返回生成的文件
return x;
}

generate函数目的为生成文件。需要传入一个用户名字符串,然后传回一个对应文件。

函数本体较为简单,通过formatter.format函数获得当前时间作为文件名,然后保存到对应的文件夹下。然后将生成的文件传传出去。


file.save

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void save(Vector<String> v,File x){
try {
FileWriter myWriter = new FileWriter(x);

//通过遍历将题目保存到试卷中
for(int i=1;i<=v.size();i++){
String num="";
num=num+i+'.';
myWriter.write(num+v.get(i-1)+"\n");
myWriter.write("\n");
}

myWriter.close();
System.out.println("试卷已保存");
} catch (IOException e) {
e.printStackTrace();
}
}

save函数目的为将题目保存到对应文件内。需要传入一个保存题目的vector以及需要保存到的文件。

主体操作为通过一个for循环,将vector中的题目字符串保存到文件内。


question.promot

question接口中有三个类似的promote函数,主要目的在于不同情况下的提示语句,比较简单就不再赘述。


question.generate

题目要求

题目生成要求

不同学校要求

具体代码及分析

1
static void generate(int n,int school,String user){}

generate函数目的在于根据输入生成对应题目并利用函数保存到文件中。
需要传入题目数量n,学校类型school以及用户名user

函数内分为三个部分,一个if语句根据school生成三种类型题目,鉴于其基本相似,这里就拿高中题目部分函数进行解释。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
String []  primary={"+","-","*","/"};//保存小学操作符号
while (n!=0){
StringBuffer s = new StringBuffer();
String inter="";//题目临时保存变量

int use=r.nextInt(6);//通过随机数生成操作数目
int size=0;//随机生成括号包含操作数数量
int before=0;//随机生成括号前方未包含操作数
if(use<2) use=2;//避免出现无操作数的情况

//使用随机数决定本题是否存在括号,当大于0且总操作数大于2时才添加括号
int flag=r.nextInt(5);
if(flag>0){
if(use>2){
size=r.nextInt(use-1);
if(size<2) size=2;
before=r.nextInt(use-size);
}
else flag=0;
}
if(before==0&&flag!=0) s.append('(');

//生成题目主体
int num=r.nextInt(99)+1;//加1为确保不出现操作数为0的情况
s.append(num);//生成初始操作数
for(int j=2;j<=use;j++){//进行随机出题
int use_index=r.nextInt(3);
s.append(primary[use_index]);//生成操作符号
if(j-1==before&&flag!=0) s.append('(');//添加左括号
num=r.nextInt(99)+1;//加1为确保不出现操作数为0的情况
s.append(num);//生成操作数
if(j==(before+size)) s.append(')');//添加右括号
}

s.append('=');//末尾添加等于
inter=s.toString();//转换为String

//查重并保存
//当成功插入后,save函数返回true值,此时题目量n减1,成功出题
if(mysql.save(inter,user)==true) {
n-=1;
v.add(inter);
}
}
for (int i=0;i<v.size();i++){//输出
System.out.println(v.get(i));
}
File x=file.generate(user);//生成文件
file.save(v,x);//文件保存

小学部分先利用随机数生成题目需要的操作数以及操作符号和是否需要括号,然后根据括号位置决定是否先在题目字符串前方添加左括号,然后将第一个操作数添加到题目字符串左方。

然后进行将题目数量n是否为0作为判断条件的while循环,每次生成操作数以及操作符号添加到题目字符串末尾,并在添加操作数和操作符号之间时判断是否需要添加括号。

生成整个题目后,利用mysql.save函数判断是否重复题目,如果没有重复则n-1,继续;如果重复则n不变。

最后将题目通过file.save和file.generate函数保存到文件里。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int junior=r.nextInt(use)+1;//生成初中符号位置
if(junior==1) {//以奇偶决定,偶则在前方生成根号,奇则在后方生成平方
if(junior/2==0) s.append('√');
}

//生成题目主体
for(int j=2;j<=use;j++){//进行随机出题
if(j==junior) {//添加平方
if(junior/2!=0) s.append('²');
}
···
if(j==junior) {//添加开根
if(junior/2==0) s.append('√');
}
···
}

初中部分增加要求为开根号和开方,先通过随机数决定符号位置,然后根据位置奇偶决定在操作数上添加开方或者开根。然后根据符号类型,判读添加在操作数前方或者后方。


1
2
3
4
5
6
7
8
9
10
11
12
//生成高中符号
int senior=r.nextInt(use)+1;//生成高中符号位置
int type=r.nextInt(3);//生成符号类型
String [] tri={"sin","cos","tan"};
if(senior==1) {
s.append(tri[type]);
}
for(int j=2;j<=use;j++){//进行随机出题
···
if (j==senior) s.append(tri[type]);//添加高中符号
···
}

高中部分增加三角函数,和初中部分一样,通过随机函数生成符号位置,然后根据位置决定符号类型,然后在对应位置直接添加到操作数前方即可。


总结

这个个人项目其实也不是很难,麻烦在于好多东西自己都没有接触过,比如数据库连接,java文件生成之类的。但好在网上相关教程还是比较多的,学习学习也就会了。希望自己能够多多学习,早日走上程序猿的岗位,然后回家开摆。