❀❀❀ 大佬求个关注吧~祝您开心每一天 ❀❀❀
目录
今天学习到了Java中的一个小项目,学生管理系统,对于新手来说,从一开始的语法语义学习,跳转到项目学习还是有点吃力的,在学习的过程中我也遇到了一些比较不理解的地方,所以梳理出一篇文章,来记录自己实现学生管理系统的过程,和自己对一些语法语义的理解。
一、ArrayList的基本使用
在学习学生管理系统之前,首先要了解ArrayList的基本使用。已经了解的可以跳过这一部分
1.1 什么是ArrayList
ArrayList是Java中一个非常的集合类,可以把它看作为一种动态数组,并且可以存储任意类型的元素,他有以下一些特点。
- 动态大小:与传统数组不同,ArrayList的大小能够动态调整。当添加元素使数组容量不足时,ArrayList会自动扩容。
- 允许存储重复元素:你可以在ArrayList里存储多个相同的元素。
- 有序集合:元素会按照插入的顺序进行存储,你能够通过索引访问特定位置的元素。
- 可存储不同类型元素:借助泛型,你既可以指定ArrayList存储特定类型的元素,也可以存储
Object
类型的元素,从而容纳不同类型的数据。
1.2 ArrayList中的一些常用方法
1.2.1 ArrayList的初始化
ArrayList作为一个类,自然有他的初始化方式,这里介绍两种ArrayList的初始化方式。
无参初始化
在创建ArrayList数组的时候,可以选择无参构造方式进行对象的创建。下方就是源码中的无参构造函数,上面提到了可以把ArrayList看作是一种动态数组,是因为ArrayList的底层就是Object数组,只是ArrayList在使用的时候可以自动的帮我们对元素进行扩容,因此是动态的。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
}
有参构造方式
创建ArrayList的时候还可以传入一些参数,我们只看下方的构造方式。
创建ArrayList对象时,可以传入一个Collection类型的变量,这个Collection类型的变量,其实就是要传入的一个数组,我们把一个已经存在的数组,作为参数传入我们要创建的ArrayList构造方法中,实现值的复制操作。
那么有哪些类可以作为Collection对象传入呢?这是一个实现Collection的树形结构图,我们可以看到Set和List都实现了Collection接口,而我们的ArrayList实现了List接口,所以我们可以把一个已经存在的ArrayList当作参数进行传入。其他的方式大家可以自己验证,只要是实现了Set和List接口的都可以作为参数传递。
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("aa");
list1.add("bb");
list1.add("cc");
List<String> list2 = new ArrayList<>(list1);
}
1.2.2 元素添加 add()和addAll()
首先是add()操作,add用于向ArrayList中添加元素,一次只能添加一个元素。这个操作比较简单,但是需要主义的是,添加的元素必须是在初始化ArrayList是指定的泛型的类型,也就是ArrayList中只可以添加一种类型的元素,不可以添加了String在添加Integer。
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("aa");
list1.add("bb");
list1.add("cc");
for (String s : list1) {
System.out.println(s);
}
}
然后是addAll()操作,有的时候,我们要把一个ArrayList中的数据,搬到另一个ArrayList中,对于新手来说可能会用到下边的方式。
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("aa");
list1.add("bb");
list1.add("cc");
List<String> list2 = new ArrayList<>();
for (String s : list1) {
list2.add(s);
}
}
但这种方式是多余的,ArrayList提供给我们addAll()方法去解决这个问题。参数同样是Collection类型,也就是说,ArrayList和Set类型的变量都可以作为参数传递。
1.2.3 元素获取get()
上方写过的代码中,遍历ArrayList的方式是通过增强for循环实现的,那么如何通过普通for循环实现呢?之前说到ArrayList是一个动态数组,既然是数组,那么应该是可以通过下标访问的,但是和普通数组的访问方式不同,ArrayList通过下标访问元素要通过get()方法,get的传参就是元素的索引位置。
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("aa");
list1.add("bb");
list1.add("cc");
for (int i = 0; i < list1.size(); i++) {
String s = list1.get(i);
System.out.println(s);
}
}
1.2.4 元素删除remove()
元素删除操作可以通过remove()方法实现,remove方法需要传入一个参数,但是看到下边的方法中,有两个remove方法,这个就是方法重载,如果我们传入的是一个int类型的变量,就会删除对应索引位置的元素,否则的话就寻找ArrayList中的那个元素,然后删掉。
现在我们来试一下。 看看我们要删除aa的方式。
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("aa");
list1.add("bb");
list1.add("cc");
for (int i = 0; i < list1.size(); i++) {
String s = list1.get(i);
System.out.println(s);
}
list1.remove("aa");
//list1.remove(0);
}
那么就会有一个问题,如果我们的ArrayList存储的就是int类型的变量呢?那么他会怎样删除呢?这里可以测试一下。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.remove(1);
for (Integer integer : list) {
System.out.println(integer);
}
}
可以看到元素2被删除了,说明删除的是按照索引来的。再测试一下下边的情况。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(4);
list.remove(4);
for (Integer integer : list) {
System.out.println(integer);
}
}
这样的情况,并不会删除元素4,而是会报错,因为有方法重载,我们传入的是数字4,是一个int类型的变量,会去寻找索引位置为4的元素,但是我们只有三个元素,所以会有索引越界。
那这样的情况要如何删除呢,要把数字4包装成一个Object类型的变量,Object?如何包装成Objec同类型呢,这里就是Java中的多态了,我们可以传入一个Object的子类,也就是可以传入一个Integer类型的变量。这样就可以了。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(4);
list.remove(new Integer(4));
for (Integer integer : list) {
System.out.println(integer);
}
}
先简单介绍上边的这些方法,这个是学生管理系统需要的一些方法。
二、学生管理系统
介绍完了ArrayList的基本应用,现在开始着手项目。
2.1 学生信息类的定义
我们需要学生的姓名、学号以及语文数学和英语成绩,作为管理系统的基础。
public class Student {
// 姓名
private String name;
//学号
private String number;
// 语文成绩
private Integer chineseScore;
// 数学成绩
private Integer mathScore;
// 英语成绩
private Integer englishScore;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Integer getChineseScore() {
return chineseScore;
}
public void setChineseScore(Integer chineseScore) {
this.chineseScore = chineseScore;
}
public Integer getMathScore() {
return mathScore;
}
public void setMathScore(Integer mathScore) {
this.mathScore = mathScore;
}
public Integer getEnglishScore() {
return englishScore;
}
public void setEnglishScore(Integer englishScore) {
this.englishScore = englishScore;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", number='" + number + '\'' +
", chineseScore=" + chineseScore +
", mathScore=" + mathScore +
", englishScore=" + englishScore +
'}';
}
}
2.2 菜单功能功能打印
对于当前系统,为了便于学习,我们只设置以下几个功能
- 学生信息的查找
- 学生信息的添加
- 学生信息的更新
- 学生信息的删除
我们先用输出语句打印一个菜单看看效果。
System.out.println("┌──────────────────────────┐");
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("│ 0. 退出系统 │");
System.out.println("└──────────────────────────┘");
看起来效果还不错,就先这样使用。
不过,当前的菜单代码是在main方法中直接输出的,作为Java学习者,我们更多的要接触面向对象的思想,也就是封装, 对于一些具有独立逻辑的代码,应该将其抽取,作为方法存在。
public static void printMenu(){
// 打印菜单界面
System.out.println("┌──────────────────────────┐");
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("│ 0. 退出系统 │");
System.out.println("└──────────────────────────┘");
}
2.3 switch控制菜单选择
有了菜单,我们就要选择菜单中具有的功能选项。
这里就用到了之前学过的siwtch条件分支。
public static void main(String[] args) {
printMenu();
int option;
Scanner sc = new Scanner(System.in);
System.out.print("请选择要进行的操作:");
option = sc.nextInt();
switch (option){
case 1:
System.out.println("查找学生信息");
break;
case 2:
System.out.println("添加学生信息");
break;
case 3:
System.out.println("更新学生信息");
break;
case 4:
System.out.println("删除学生信息");
break;
case 0:
System.out.println("退出系统");
break;
default:
break;
}
}
到了这一步,已经有了管理系统的雏形了,我们可以根据输入的数组去选择对应的功能。但是现在还有一个问题,当我们选择了某个功能后,对应的方法执行完毕,就会直接让程序结束,这是我们不希望看到的结果,不然每选择一个功能就要退出去程序。
2.4 while控制菜单循环打印
我们只要把输入选项的功能,放在while中就可以解决这个问题,当我们选择功能0的时候,在退出系统,这时候我们可以直接调用System.exit(0)这个方法,退出程序。
public static void main(String[] args) {
printMenu();
while(true){
int option;
Scanner sc = new Scanner(System.in);
System.out.print("请选择要进行的操作:");
option = sc.nextInt();
switch (option){
case 1:
System.out.println("查找学生信息");
break;
case 2:
System.out.println("添加学生信息");
break;
case 3:
System.out.println("更新学生信息");
break;
case 4:
System.out.println("删除学生信息");
break;
case 5:
System.out.println("退出系统");
System.exit(0);
break;
default:
System.out.println("请输入正确的选项");
break;
}
}
现在在运行程序,就可以对刚才的菜单进行测试了。
现在就可以进行学生管理系统的编写了。
在上面讲到了,我们对于具有独立功能的代码,要将其抽取出,作为一个方法存在,对于学生的查找、添加、更新和删除操作,我们都应该将其抽取作为独立的方法存在。
2.5 学生信息的添加
为了添加学生的信息,我们需要有一个ArrayList去保存学生的信息,我们需要在main方法中定义出(当然,也可以直接写为静态变量,这样方法就不需要传参了)。
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
}
在下方的代码中,学生的操作都是围绕这个students列表来的。
我们创建出新的方法addStudent()。这个方法需要两个参数,一个是保存学生信息的集合,另外一个参数是我们要添加的学生的信息。
public static void addStudent(List<Student> students) {
}
至于这里为什么要写第一个参数,第一个参数和我们定义的学生的列表有什么关系,这里简单的介绍一下。
在Java当中,方法传参是值传递,什么是值传递呢?
先介绍一下,List是如何定义的,在Java中,通俗易懂的讲,通过new创建出来的对象,在内存中是有专门的空间来保存的,就像上边我们定义的students列表,它就是在内存中的一块区域保存的。
看上方的图,在存储区域,我们真正的保存这个列表,而在main区域,我们保存的只是students的地址。 什么是地址呢,就好比一个电话簿,里边记录着很多人的地址,通过地址我们就能找到那个人,这个我们定义的列表有什么关系呢?
这样想,有一个班的同学感情都非常好,大家毕业了,都不想离开教室,因为大家想要一直见面,不想分开。大家都在教室里生活,但是有天发现,教室的位置不够大了,大家在教室里生活了三年,有的人生了娃(假设),老师这时候发话了:“我们的教室位置不够了,我建议大家各回各家,我们把自己的地址记录在每个人的课桌上,这样我们想要见到那个人的时候,就可以根据课桌上的地址找到了”。这样一来,教室的人全都走掉了,大家都在自己家里,空间很大。
main方法和存储区域也是一样的,main方法是存放在栈中的,而对象是存储在堆中的,堆要比栈大得多,所以对象不可以存储在栈中。
这样一来,只要在栈中记录一下列表在堆区的地址就好了,地址占用的空间很小,通过地址就可以顺利的找到列表。
这和值传递有什么关系呢?
值传递,值传递,传递的就是值,但我们在main方法中调用添加学生的方法的时候,会把学生的列表作为参数传递,这里传递的其实是学生列表的地址,addStudent()方法,拿到这个地址后,就可以顺利的找到,学生列表在内存中存储的区域。
知道了这一点,就开始编写学生添加的方法。
注释很清晰。
public static void addStudent(List<Student> students) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入学生的学号:");
String number = sc.next();
// 在我们输入了学生的学号后,还要检查一下是不是已经有这个学生的学号了,我们保证学号唯一
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getNumber().equals(number)) {
// 证明学生已经存在了
System.out.println("该学生已经存在,请重新尝试\n");
// 直接返回,因为学生已经存在了,我们不需要在执行方法内的任何逻辑,直接结束这个方法
return;
}
}
// 能走到这里证明我们输入的学生的学号是没有被存储过的
System.out.print("请输入学生的姓名:");
String name = sc.next();
System.out.print("请输入学生的语文成绩:");
int chineseScore = sc.nextInt();
System.out.print("请输入学生的数学成绩:");
int mathScore = sc.nextInt();
System.out.print("请输入学生的英语成绩:");
int englishScore = sc.nextInt();
// 创建学生对象
Student student = new Student();
student.setNumber(number);
student.setName(name);
student.setChineseScore(chineseScore);
student.setMathScore(mathScore);
student.setEnglishScore(englishScore);
// 保存学生信息
students.add(student);
System.out.println("学生信息添加成功\n");
}
2.6 学生信息的查找
添加了学生信息的目的,就是为了查找,查找的代码如下。
public static void getStudentInfo(List<Student> students){
Scanner sc = new Scanner(System.in);
System.out.print("请输入学生的学号:");
String number = sc.next();
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getNumber().equals(number)) {
Student student = students.get(i);
// 说明找到了学生的学号,集合中添加过这个学生
System.out.println("学生信息如下:");
System.out.printf("%-10s %-10s %-10s %-10s %-10s\n", "学号", "姓名", "语文成绩", "数学成绩", "英语成绩");
System.out.printf("%-10s %-10s %-10d %-10d %-10d\n",
student.getNumber(), student.getName(), student.getChineseScore(), student.getMathScore(), student.getEnglishScore());
// 找到了学生信息,直接结束方法就可以
return;
}
}
// 如果走到了这里,说明遍历完了学生列表,还是没找到学生的信息
System.out.println("该学生不存在,请重新尝试\n");
}
2.7 学生信息的删除
public static void deleteStudentInfo(List<Student> students){
Scanner sc = new Scanner(System.in);
System.out.print("请输入学生的学号:");
String number = sc.next();
// 首先检查一下有没有学生的信息
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getNumber().equals(number)) {
// 说明找到了学生的学号,集合中添加过这个学生
students.remove(i);
System.out.println("学生信息删除成功\n");
return;
}
}
// 如果走到了这里,说明还没有添加这个学生的信息
System.out.println("该学生不存在,请重新尝试\n");
}
2.8 学生信息的更新
public static void updateStudentInfo(List<Student> students){
Scanner sc = new Scanner(System.in);
System.out.print("请输入学生的学号:");
String number = sc.next();
// 首先检查一下有没有学生的信息
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getNumber().equals(number)) {
Student student = students.get(i);
// 说明找到了学生的学号,集合中添加过这个学生
// 先打印一下这个学生的信息
System.out.println("当前学生信息如下:");
System.out.printf("%-10s %-10s %-10s %-10s %-10s\n", "学号", "姓名", "语文成绩", "数学成绩", "英语成绩");
System.out.printf("%-10s %-10s %-10d %-10d %-10d\n",
student.getNumber(), student.getName(), student.getChineseScore(), student.getMathScore(), student.getEnglishScore());
// 然后让用户输入新的学生信息
System.out.print("请输入学生的姓名:");
String name = sc.next();
System.out.print("请输入学生的语文成绩:");
int chineseScore = sc.nextInt();
System.out.print("请输入学生的数学成绩:");
int mathScore = sc.nextInt();
System.out.print("请输入学生的英语成绩:");
int englishScore = sc.nextInt();
// 修改学生信息
student.setName(name);
student.setChineseScore(chineseScore);
student.setMathScore(mathScore);
student.setEnglishScore(englishScore);
System.out.println("学生信息修改成功\n");
return;
}
}
// 如果走到了这里,说明还没有添加这个学生的信息
System.out.println("该学生不存在,请重新尝试\n");
}
由于篇幅限制,以后有机会的话,把这个学生管理系统修改为进阶版本的。当然这个版本的代码,还有很多不充分的地方,下期会一一解决。
2.9 打印所有学生信息
大家可以自己加一下这个功能。
public static void printAllStudentInfo(List<Student> students){
System.out.println("学生信息如下:");
System.out.printf("%-10s %-10s %-10s %-10s %-10s\n", "学号", "姓名", "语文成绩", "数学成绩", "英语成绩");
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
System.out.printf("%-10s %-10s %-10d %-10d %-10d\n",
student.getNumber(), student.getName(), student.getChineseScore(), student.getMathScore(), student.getEnglishScore());
}
}