【JAVA进化论】LV2-7:【案例】学生信息管理系统

学完了类,我们利用类,结合现有的软件设计的分层思想,来做一个基本的学生信息管理系统,因为我们还没有接触数据库,所以我们利用数组来做基本的存储,该系统拥有的功能如下:

  1. 欢迎页面,介绍功能点、指令
  2. 指令1,新增学生信息,输入完最后一个学生属性后,输出“ok,已完成录入”的信息,然后回到欢迎页
  3. 指令2,修改学生信息,跟1一样,输入学生id确定需要修改的学生,首先输出其信息,然后输入一个个的新属性值直到完最后一个学生属性后,输出“ok,已完成修改”的信息,然后回到欢迎页
  4. 指令3,删除学生信息,输入学生id,删除对应的学生信息,删除后,输出“ok,已删除”,然后回到欢迎页
  5. 指令4,根据id查询学生信息,输入学生id,找到其信息,然后输出其信息,然后回到欢迎页
  6. 指令5,输出所有学生的信息,最好以一个表格的形式展示,然后回到欢迎页
  7. 指令0,终止程序

首先我们来看下项目的基本结构:

图1

一、Model层

首先抽象好一个Person类来做Student的父类:

代码块1
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
package student.system.model;

//这里定义一个父类,定义人最基本的属性,为什么要把人独立出来做父类?如果以后还有老师类,就不用再写一遍下面的属性了啊,直接继承即可
public class Person {

//名字
private String name;
//年龄
private int age;
//性别:0女性,1男性
private int gender;
//住址
private String address;

//下面就是用来设置属性值和获取属性值的方法
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public int getGender() {
return gender;
}

public void setGender(int gender) {
this.gender = gender;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}
}

这里面存放学生作为「人」的基本属性。

定义一个班级类,用来表示学生所在的班级,最终它要和学生类关联起来:

代码块2
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
package student.system.model;

//班级信息类
public class Grade {

//班级编号
private int id;
//班级名称
private String name;

//下面就是用来设置属性值和获取属性值的方法
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

最后定义出来学生类:

代码块3
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
package student.system.model;

//学生类,最终存放学生信息的类,学生是人,所以他们具备所有人的属性,除此之外,他们有自己的一些拓展属性
public class Student extends Person {
//学号
private int id;
//平均成绩
private float avg_score;
//所属班级
private Grade grade;

//下面就是用来设置属性值和获取属性值的方法
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public float getAvg_score() {
return avg_score;
}

public void setAvg_score(float avg_score) {
this.avg_score = avg_score;
}

public Grade getGrade() {
return grade;
}

public void setGrade(Grade grade) {
this.grade = grade;
}
}

可以看到,学号、成绩这些都是学生特有的,像什么性别年龄这些,都是作为「人」共有的,为了达到复用,我们把它们抽了出来放到Person类,日后如果需要拓展一个老师类,那么老师也符合这些信息,直接继承复用即可。

二、Dao层

这一层用来模拟数据库存储,主要用来实现增删改查最基本的元操作:

代码块4
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package student.system.dao;

import student.system.model.Student;

//这个类位于我们定义的dao层,dao层一般负责和数据库交互进行最基本的增删改查操作
//由于我们没有数据库,就在dao层这个类里定义一个数组用来保存student信息吧
public class StudentDao {

//在实际的项目里,你可以理解这个数组相当于某个数据库的student表
private Student[] students = new Student[10]; //假如数据库的容量仅为10,超过这个量,就会报错

private int studentsNum = 0; //数组下标

//增:用来添加student信息进students里
public boolean add(Student student) { //接收一个Student类型的参数,这个student就是需要add方法做存储处理的
if (isOverflow()) {
System.out.println("数据库已满,添加失败!");
return false;
} else {
students[studentsNum] = student; //保存这个student
studentsNum++; //为了便于下一个数据的存储且不覆盖当前位置的数据,这里下标自增1
return true;
}
}

//删:按照学生的id进行删除该学生信息
public boolean del(int id) {
//ps:数组做删除是件非常麻烦的事情-_-||,这里偷懒了,只需要知道该方法可以完成一个数据的删除即可
Student[] newArray = new Student[10];
int j = 0;
for (int i = 0; i < studentsNum; i++) {
if (students[i].getId() != id) {
newArray[j] = students[i];
j++;
}
}
students = newArray;
studentsNum--; //删除掉一个数据,下标自减1
return true;
}

//改:根据输入的新的学生信息,按照id替换掉原先对应的学生信息
public boolean update(Student student) {
boolean result = false;
for (int i = 0; i < studentsNum; i++) {
if (students[i] != null && students[i].getId() == student.getId()) {
students[i] = student;
result = true;
break; //终止循环
}
}
return result;
}

//查:按照学生id,查询学生信息
public Student getStudentById(int id) {
for (int i = 0; i < studentsNum; i++) {
if (students[i] != null && students[i].getId() == id) {
return students[i]; //找到了直接返回出去
}
}
return null; //找不到就返回空对象
}

//列表:返回当前所有的学生信息
public Student[] getAllStudents() {
Student[] nowStudents = new Student[studentsNum];
//这个操作会把students里下标为0~studentsNum的元素copy给nowStudents
System.arraycopy(students, 0, nowStudents, 0, studentsNum);
return nowStudents;
}

//用于内部判断当前数据库是不是已经存满数据了
private boolean isOverflow() {
return studentsNum == 10;
}

}

三、Service层

这一层主要跟Dao层交互,通常会利用Dao层里的元操作来完成更加复杂的业务性质的操作:

代码块5
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
50
51
52
package student.system.service;

import student.system.dao.StudentDao;
import student.system.model.Student;

//service层的类,它触发Dao层的基本操作的同时,还会用来处理更加复杂的业务逻辑
public class StudentService {

private StudentDao studentDao = new StudentDao(); //因为service层需要做基本的增删改查操作,因此它持有一个StudentDao类型的属性

//这个方法用来触发Dao层的"增"方法
public boolean add(Student student) {
if (student == null) {
System.out.println("请输入合理的student!");
return false;
}
//你会发现不就是简单触发一下Dao层的add方法吗?为啥需要单独拆一个Service层来套娃呢?因为我上面解释过了,service是用来处理复杂业务逻辑的
//真正的service层可能会引很多Dao层进行混合操作,本例之所以这么简单是因为这个系统本身就很简单啊。。理解这个点就好~
return studentDao.add(student);
}

//这个方法用来触发Dao层的"删"方法
public boolean del(int id) {
if (id <= 0) {
System.out.println("请输入合理的id!");
return false;
}
return studentDao.del(id);
}

//这个方法用来触发Dao层的"改"方法
public boolean update(Student student) {
if (student == null) { //这块代码跟add里的重复,理论上可以封装成一个独立的方法,但这里我们不这么干了。。所以说有些规则只是理想化下会选择这么做,但实际开发中可能并没有这么严格
System.out.println("请输入合理的student!");
return false;
}
return studentDao.update(student);
}

//这个方法用来触发Dao层的"查"方法
public Student getStudentById(int id) {
if (id <= 0) {
return null;
}
return studentDao.getStudentById(id);
}

//这个方法用来触发Dao层的"列表"方法
public Student[] getAllStudents() {
return studentDao.getAllStudents();
}
}

四、View层

这一层负责和用户交互,将用户输入的指令传达给Service,并将Service的处理结果通知给用户,这一层在企业级软件设计里被称为视图层(虽然MVC在很多场景下已经“过时”,但是我们仍然可以从中学到企业级开发时所需要的分层思想):

代码块6
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package student.system.view;

import student.system.model.Grade;
import student.system.model.Student;
import student.system.service.StudentService;

import java.util.Scanner;

//视图层,用来展示给用户看的
public class StudentSystemView {

//因为视图层需要请求基本的增删改查操作,因此它持有一个StudentService类型的属性
private StudentService studentService = new StudentService();

public static void main(String[] args) {
StudentSystemView view = new StudentSystemView();
while (true) {
int opNum = view.home();
if (opNum == 0) { //终止程序
break;
} else if (opNum == 1) {
//需要新增学生信息
view.addStudent();
} else if (opNum == 2) {
view.updateStudent();
} else if (opNum == 3) {
view.delStudent();
} else if (opNum == 4) {
view.getStudentById();
} else if (opNum == 5) {
view.getAllStudents();
} else {
System.out.println("请输入有效的指令!");
}
}

}

//首页
public int home() {
System.out.println("+----------------------------------");
System.out.println("| S大学-学生信息管理系统");
System.out.println("+----------------------------------");
System.out.println("| 指令集:");
System.out.println("| 1.新增");
System.out.println("| 2.修改");
System.out.println("| 3.删除");
System.out.println("| 4.查找");
System.out.println("| 5.学生列表");
System.out.println("| 0.终止程序");
System.out.println("+----------------------------------");
System.out.println("请输入您的操作:");
Scanner s = new Scanner(System.in);
return s.nextInt();
}

//添加学生信息
public void addStudent() {
System.out.println("您现在正在添加一个学生的信息,请按照指令输入学生的各项信息:");
Student student = scannerStudent();
boolean result = studentService.add(student); //最后我们通过studentService将这个学生信息加到"数据库"里去
//按照service层的处理结果,输出不同的文案
if (result) {
System.out.println("数据添加成功!");
} else {
System.out.println("数据添加失败!");
}
}

//删除学生信息
public void delStudent() {
System.out.println("您现在正在删除一个学生的信息,请输入需要删除学生的id:");
Scanner s = new Scanner(System.in);
int id = s.nextInt();
boolean result = studentService.del(id); //最后我们通过studentService将这个学生信息加到"数据库"里去

//按照service层的处理结果,输出不同的文案
if (result) {
System.out.println("数据删除成功!");
} else {
System.out.println("数据删除失败!");
}
}

//修改学生信息
public void updateStudent() {
System.out.println("您现在正在修改一个学生的信息,请按照指令输入学生的各项信息:");
Student student = scannerStudent();
boolean result = studentService.update(student); //最后我们通过studentService将对应的学生信息修改掉
//按照service层的处理结果,输出不同的文案
if (result) {
System.out.println("数据修改成功!");
} else {
System.out.println("数据修改失败!");
}
}

//按照id查找学生信息
public void getStudentById() {
System.out.println("请输入需要查找学生的id:");
Scanner s = new Scanner(System.in);
int id = s.nextInt();
Student result = studentService.getStudentById(id); //最后我们通过studentService查找到对应的学生信息
//按照service层的处理结果,输出不同的文案
if (result != null) {
printStudentInfo(result);
} else {
System.out.println("未查到对应数据!");
}
}

//输出当前"数据库"内所有学生的信息
public void getAllStudents() {
Student[] result = studentService.getAllStudents(); //最后我们通过studentService将所有的学生信息拿到
//按照service层的处理结果,输出不同的文案
if (result != null) {
for (Student student : result) {
printStudentInfo(student);
}
} else {
System.out.println("未查到对应数据!");
}
}

//用来让用户输入一个学生的信息,由于修改信息那里以及新增信息那里都需要这个操作,而且这个操作代码又很长,因此为了复用这块的代码,我们把它封装出来,以供别人重复使用
private Student scannerStudent() {
System.out.println("请输入学生的id:");
Scanner s = new Scanner(System.in);
int id = s.nextInt();
System.out.println("请输入学生的名字:");
String name = s.next();
System.out.println("请输入学生的年龄:");
int age = s.nextInt();
System.out.println("请输入学生的性别(0女1男):");
int gender = s.nextInt();
System.out.println("请输入学生的住址:");
String address = s.next();
System.out.println("请输入学生的平均分:");
float avgScore = s.nextFloat();
System.out.println("请输入学生所属班级的id:");
int gradeId = s.nextInt();
System.out.println("请输入学生所属班级的名称:");
String gradeName = s.next();

//开始根据输入的信息组装学生对象
Student student = new Student();
student.setId(id);
student.setName(name);
student.setAge(age);
student.setGender(gender);
student.setAddress(address);
student.setAvg_score(avgScore);
//构建一个班级类
Grade grade = new Grade();
grade.setId(gradeId);
grade.setName(gradeName);
student.setGrade(grade); //给student对象里持有的grade属性赋值
return student;
}

//按照某种格式打印出来一个student信息的方法,在getStudentById和getAllStudents中均有使用
private void printStudentInfo(Student student) {
System.out.println("id = " + student.getId()
+ "\t name = " + student.getName()
+ "\t age = " + student.getAge()
+ "\t address = " + student.getAddress()
+ "\t gender = " + (student.getGender() == 1 ? "男" : "女")
+ "\t avg_score = " + student.getAvg_score()
+ "\t address = " + student.getAddress()
+ "\t grade_id = " + student.getGrade().getId()
+ "\t grade_name = " + student.getGrade().getName());
}

}

六、运行结果

直接运行view层代码的main方法,开始录入学生并完成一次查找:

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
录入学生信息&查找学生信息

+----------------------------------
| S大学-学生信息管理系统
+----------------------------------
| 指令集:
| 1.新增
| 2.修改
| 3.删除
| 4.查找
| 5.学生列表
| 0.终止程序
+----------------------------------
请输入您的操作:
1
您现在正在添加一个学生的信息,请按照指令输入学生的各项信息:
请输入学生的id:
1
请输入学生的名字:
s
请输入学生的年龄:
26
请输入学生的性别(0女1男):
1
请输入学生的住址:
shanghai
请输入学生的平均分:
100
请输入学生所属班级的id:
1
请输入学生所属班级的名称:
2.1
数据添加成功!
+----------------------------------
| S大学-学生信息管理系统
+----------------------------------
| 指令集:
| 1.新增
| 2.修改
| 3.删除
| 4.查找
| 5.学生列表
| 0.终止程序
+----------------------------------
请输入您的操作:
4
请输入需要查找学生的id:
1
id = 1 name = s age = 26 address = shanghai gender = 男 avg_score = 100.0 address = shanghai grade_id = 1 grade_name = 2.1

你可以自己模仿着写一个类似这种指令输入方式的“xxx管理系统”,然后自己可以运行下,感受下。