简单基础的学生管理系统项目

学生管理系统项目【所有知识点整合】

1.学生管理系统

需求:

完成以下功能
实体类:学生类(成员变量有id, 姓名,年龄,性别,成绩)
需要使用数组保存学生信息: Student[] allStus
需要完成的方法
1. 根据学生的ID,找到对应的学生对象
2. 完成方法,添加新学生
3. 完成方法,删除指定ID的学生
4. 完成方法,展示数组中所有的学生信息
5. 根据学生成绩,完成降序排序

1.1 包结构划分
  1. 包名规范:

    1. 所有的单词全部小写
    2. 不同的单词直接使用 . 隔开
    3. 包结构其实对应的就是一个真实的目录结构
  2. 包结构的使用是为了在开发中让代码结构更加明确,更好管理,会接触到MVC设计模式(MVC ==> Module Viewer Controller)

  3. 目前学生管理系统需要的包【目前功能所需】

    实体类:所有的实体类都会在一个包下

    管理类:需要一个管理类来管理学生操作(核心)需要一个包

    主方法类:主方法

    测试类:测试功能,养成习惯,对于代码中的功能,写一个测试一个,今天会用到@Test

  4. 包名

    com.qfedu.student.system.

    ​ – | entity 实体类包

    ​ – | manager 管理类包

    ​ – | mainproject 主方法所在包

    ​ – | testsystem 测试类

1.2 学生实体类
package com.qfedu.student.system.entity;

/**
 * 学生实体类
 * @author Mr_SunJH
 *
 */
public class Student {
	private int id;
	private String name;
	private int age;
	private char gender;
	private int score;
	
	public Student() {}

	public Student(int id, String name, int age, char gender, int score) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.gender = gender;
		this.score = score;
	}

	
	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;
	}

	public int getAge() {
		return age;
	}

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

	public char getGender() {
		return gender;
	}

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

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}

	/**
	 * 使用System.out.println打印展示Student类对象时
	 * 是直接调用toString方法,展示该方法返回String字符串内容
	 */
	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + ", score=" + score
				+ "]";
	}
	
}
1.3 管理类功能分析

管理类功能:

  1. 数据的保存

    明确使用的是一个Student类的数据:Student[] allStus;

    作为成员变量,还是静态成员变量问题:

    (1)成员变量:使用一个成员变量数组来保存对应的数据,所有的内容都依赖于类对象的操作来完成,而且每一个管理类对象中保存的数据都是不一样的。目前的不一样,是为了更好的复用性,后面的数据一样,是为了统一性。【选用成员变量】

    (2)静态成员变量:不管有多少个类对象,当前数据有且只有一份。存在复用问题,因为当前管理类,在后期功能是考虑复用的,不管是数据的存储方式,数据的处理方式,都是需要来考虑满足多种情况,多种方式。

  2. 数据处理:CRUD 增删改查

1.4 管理类构造方法
  1. 因为当前管理类内成员变量是一个数组,当前构造方法需要对于保存学生信息的数组进行初始化操作

    (1)传入参数是一个学生数组【不选】

    (2)传入参数是一个数组容量

  2. 分析:

    (1)传入一个数组,操作性、安全性都是存在一定的隐患,操作性较差,用户需要提供真实管理数据的空间,繁琐,引用指向有可能导致数据丢失

    (2)传入一个数组容量,用户操作的自由度更高,方便,快捷,省心省事

  3. 要求传入的参数是一个数组容量,有没有要求???

    (1)int

    (2)非负

    (3)int类型最大值

    【补充】Java中数组容量是在int范围以内,要求数组容量不得超出int范围 Integer.MAX_VALUE - 8(为什么-8 后面解释)

  4. 构造方法这里提供两种

    (1)无参数构造方法:用户不用指定容量,我们给予用户一个初始化容量使用

    (2)有参数构造方法:用户指定底层数组容量,要求数据在合理范围以内

1.5 构造方法完成
package com.qfedu.student.system.manager;

import com.qfedu.student.system.entity.Student;

/**
 * 学生管理类
 * @author Mr_SunJH
 *
 */
public class StudentManager {
	/*
	 * 私有化保存学生信息的数组,对外不能公开,有且只针对与当前管理类使用
	 */
	private Student[] allStus = null;
	// DEFAULT_CAPACITY 默认容量,这里是一个带有名字的常量
	private static final int DEFAULT_CAPACITY = 10;
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	/*
	 * 无参数构造方法,但是需要创建底层保存学生数据的Student数组,因为当前数组
	 * 不存在,指向为null
	 * 
	 * 默认初始化容量为10,10是一个常量数据,操作阅读不太方便
	 */
	public StudentManager() {
		allStus = new Student[DEFAULT_CAPACITY];
	}
	
	/**
	 * 用户指定初始化容量,但是要求初始化容量是在合理范围内,不能小于0,
	 * 不能大于数组的最大值MAX_ARRAY_SIZE
	 * @param initCapacity 用户指定的初始化容量
	 */
	public StudentManager(int initCapacity) {
		if (initCapacity < 0 || initCapacity > MAX_ARRAY_SIZE) {
			System.out.println("Input Parameter is Invalid!");
			/* 异常抛出!!!补充该知识点,暂时留下一个System.exit(0)  退出程序 */
			System.exit(0);
		}
		allStus = new Student[initCapacity];
	}
}
1.6 增删改查方法实现
1.6.1 增【重点】
  1. 分析:

    权限修饰符:

    ​ public √

    ​ private 如果私有化,类外无法使用,不能操作数据

    是否需要static修饰

    ​ 不需要

    ​ 保存学生信息的数组是static修饰还是非static修饰?非static修饰

    ​ 如果当前方法使用static修饰,是没有办法操作类内的成员变量

    返回值类型:boolean类型。添加成功返回true,添加失败返回false

    方法名:add 添加

    形式参数列表:Student student

    方法声明

    public boolean add(Student student)

  2. 采用尾插法数据存入,需要一个计数器:int类型数据

    如果作为局部变量:方法运行结束,不存在,无法保存数据

    如果作为成员变量:可以保证每一个StudentManager对象中存储的数据是独立的

    如果作为静态成员变量:静态成员变量和类对象无关,而且独此一份,每一个StudentManager对象中保存的数据个数都是独立的,是不合理的

/**                                                     
 * 当前方法是添加学生类对象到StudentManager中,保存到底层的Student类型数组       
 *                                                      
 * @param stu Student类对象                                
 * @return 添加成功返回true,失败返回false                         
 */                                                     
public boolean add(Student stu) {                       
	allStus[size] = stu;                                
	size += 1;                                          
	                                                    
	return true;                                        
}  
  1. 目前存在问题
    (1)好像没有错误情况处理

    ​ 需要异常处理来辅助完成错误或者异常处理

    (2)容量不够怎么办?

    ​ 扩容!!!

    ​ 考虑:①数组的容量在创建之后是无法修改的;

    ​ ②重新创建一个数组!新数组要求比原本的数组容量大;

    ​ ③从原数据数组迁移数据到新数组中;

    ​ ④重新赋值成员变量allStus保存的空间地址指向新数组。

1.6.2 grow方法,底层数组容量扩容方法【核心】
  1. 流程:

    1. 计算原数组容量
    2. 计算得到新数组容量
    3. 创建新数组
    4. 迁移数据
    5. 保存新数组地址
  2. 方法分析:

    (1)权限修饰符

    ​ public :类外可以随便使用,数组容量不受控制,我们期望的是在数组添加元素的时候容量不足的情况下,才会调用当前grow方法,public不合适

    ​ private : 核心操作,主要是在类内的使用,类外不能调用当前方法

    (2)是否需要static修饰

    ​ 因为需要操作类内的底层数组,数组非static修饰,不需要使用static

    (3)返回值类型:void 当前方法不需要返回值

    (4)方法名:grow

    (5)形式参数列表

    ​ 要求最小容量

    ​ 为了保证add操作在合理的容量范围以内操作成功,这里要求通过add方法告知grow方法,最小要求容量扩容到多少?

    ​ 需要考虑一些极端情况:

    ​ ①添加多个,超出了grow增加的范围

    ​ ②原数组容量为1,扩容之后不够用

  3. 方法声明

    private void grow(int minCapacity);

	/**
	 * 类内私有化方法,用于在添加元素过程中,出现当前底层数组容量不足的情况下
	 * 对底层数组进行扩容操作,满足使用要求
	 * @param minCapacity 添加操作要求的最小容量
	 */
	private void grow(int minCapacity) {
		// 1. 获取元数组容量
		int oldCapacity = allStus.length;
		
		// 2. 计算得到新数组容量,新数组容量大约是原数组容量的1.5倍
		int newCapacity = oldCapacity + oldCapacity / 2;
		
		// 3. 判断新数组容量是否满足最小容量要求
		if (minCapacity > newCapacity) {
			newCapacity = minCapacity;
		}
		// 4. 判断当前容量是否超出了MAX_ARRAY_SIZE
		if (newCapacity > MAX_ARRAY_SIZE) {
			// 这里需要一个异常处理,目前我们采用程序退出
			System.exit(0);
		}
		
		// 5. 创建新数组
		Student[] temp = new Student[newCapacity];
		
		// 6. 数据拷贝
		for (int i = 0; i < oldCapacity; i++) {
			temp[i] = allStus[i];
		}
		
		// 7. 使用 allStus 保存新数组首地址
		allStus = temp;
	}
1.6.3 删【重点】
  1. 方法分析

    (1)权限修饰符

    ​ public √

    ​ private【不合适】

    (2)是否需要 static

    ​ 不需要

    (3)返回值类型

    ​ boolean类型 删除成功返回true,删除失败返回false

    (4)方法名:remove 移除

    (5)形式参数列表:int id

  2. 方法声明

    public boolean remove(int id)

  3. 流程

    1. 根据id找出对应的学生对象在数组中的下标位置
    2. 根据下标位置删除元素,从删除位置开始,之后的元素整体向前移动
    3. 最后一个原本存在数据的位置赋值为null
    4. size有效元素个数 -= 1
	/**
	 * 根据用户指定的id号删除对应学生类对象
	 * @param id 指定学生id
	 * @return 删除成功返回true,删除失败返回false
	 */
	public boolean remove(int id) {
		int index = -1;
		
		// 遍历数组的终止条件为size,有效元素个数
		for (int i = 0; i < size; i++) {
			if (id == allStus[i].getId()) {
				index = i;
				break;
			}
		}
		/*
		 * 以上代码循环结束,如果index的值为-1,说明没有找到对应的元素
		 * 当前方法无法进行删除操作,返回false
		 */
		if (-1 == index) {
			System.out.println("Not Found!");
			return false;
		}
		
		/**
		 * 如果 index 不是 -1 表示找到了对应需要删除的元素,进行删除操作
		 * 
		 * 假设元数组容量为10,有效元素个数为10,删除下表为5的元素
		 * 		数组[5] = 数组[6];
		 * 		数组[6] = 数组[7];
		 * 		数组[7] = 数组[8];
		 * 		数组[8] = 数组[9];
		 * 		数组[9] = null;
		 * 
		 * 		数组[i] = 数组[i + 1];
		 */
		for (int i = index; i < size; i++) {
			allStus[i] = allStus[i + 1];
		}
		// 原本最后一个有效元素位置赋值为null
		allStus[size - 1] = null;
		size -= 1;
		return true;
				
	}
1.6.4 查【重点】
  1. 方法分析

    (1)权限修饰符

    ​ public √

    ​ private

    (2)是否需要static修饰:不需要

    (3)返回值类型:Student 学生类对象

    (4)方法名 get 获取

    (5)形式参数列表:int id

  2. 方法声明

    public Student get(int id)

/**                                           
 * 根据指定的ID获取对应的Student类对象                     
 *                                            
 * @param id 指定的ID号                           
 * @return 返回对应的Student类对象, 如果没有找到,返回null     
 */                                           
public Student get(int id) {                  
	int index = findIndexById(id);            
                                              
	return index > -1 ? allStus[index] : null;
} 
1.6.5 补充方法,根据ID获取对应下标位置
  1. 方法分析

    (1)权限修饰符

    ​ public

    ​ private √ 给内部其他方法使用的一个功能,不需要对外

    (2)是否需要static修饰:不需要

    (3)返回值类型:int 类型, 需要的是一个下标

    (4)方法名:findIndexById

    (5)形式参数列表 int id

  2. 方法声明

    public int findIndexById(int id)

/**                                                        
 * 类内私有化方法,用于根据指定ID获取对应的下标位置,提供给其他方法使用                     
 *                                                         
 * @param id 指定ID号                                         
 * @return 返回值是对应的下标位置,返回值大于等于0 表示找到对应元素,返回-1没有找到          
 */                                                        
private int findIndexById(int id) {                        
	int index = -1;                                        
                                                           
	for (int i = 0; i < size; i++) {                       
		if (id == allStus[i].getId()) {                    
			index = i;                                     
			break;                                         
		}                                                  
	}                                                      
                                                           
	return index;                                          
}   
1.6.6 改
  1. 需求:

    真实修改数组中保存的学生对象数据

    需要我们找到对应学生对象,修改其中指定的数据

  2. 方法分析:

    (1)权限修饰符:public

    (2)是否需要static修饰:不需要

    (3)返回值类型:

    ​ ① Student

    ​ ② boolean:监测方法运行状态,如果出现问题,返回false

    (4)方法名:modify

    (5)形式参数列表:int id

  3. 方法声明:

    public boolean modify(int id)

【static补充】

①static修饰成员变量的共享性和持久性

②static修饰静态代码块的加载行和初始化操作

③static修饰静态成员方法操作的便捷性和独立性

/**                                                          
 * 根据用户指定的ID号,修改对应学生的信息                                   
 *                                                           
 * @param id 用户指定的ID号                                        
 * @return 方法运行成功返回true,如果修改无法完成返回false                   
 */                                                          
public boolean modify(int id) {                              
	// 通过类内其他成员方法获取对应ID的学生类对象                                
	Student stu = get(id);                                   
	                                                         
	// 没有对应的学生类对象                                            
	if (null == stu) {                                       
		System.out.println("Not Found");                     
		return false;                                        
	}                                                        
	                                                         
	// 类似于点菜系统,需要完成修改操作                                      
	Scanner sc = new Scanner(System.in);                     
	int choose = 0;                                          
	                                                         
	while (choose != 5) {                                    
		System.out.println("ID: " + stu.getId());            
		System.out.println("Name: " + stu.getName());        
		System.out.println("Age: " + stu.getAge());          
		System.out.println("Gender: " + stu.getGender());    
		System.out.println("Score: " + stu.getScore());      
		                                                     
		System.out.println("1. 修改学生姓名");                     
		System.out.println("2. 修改学生年龄");                     
		System.out.println("3. 修改学生性别");                     
		System.out.println("4. 修改学生成绩");                     
		System.out.println("5. 退出");                         
		                                                     
		choose = sc.nextInt();                               
		// 1\n ==> 1 程序中剩余\n                                 
		sc.nextLine();                                       
		                                                     
		switch (choose) {                                    
		case 1:                                              
			System.out.println("请输入学生的姓名:");                 
			String name = sc.nextLine();                     
			stu.setName(name);                               
			                                                 
			break;                                           
		case 2:                                              
			System.out.println("请输入学生的年龄:");                 
			int age = sc.nextInt();                          
			stu.setAge(age);                            
			                                                 
			break;                                           
		case 3:                                              
			System.out.println("请输入学生的性别:");                 
			char gender = sc.nextLine().charAt(0);           
			stu.setGender(gender);                           
			                                                 
			break;                                           
		case 4:                                              
			System.out.println("请输入学生的成绩:");                 
			int score = sc.nextInt();                        
			stu.setScore(score);                             
                                                             
			break;                                           
		case 5:                                              
			System.out.println("保存退出");                      
			break;                                           
                                                             
		default:                                             
			System.out.println("选择错误,请重新选择");                
			break;
		}
	}
	return true;                                             
}
1.6.7 排序【改造潜力最大的方法】
  1. 需求:

    根据学生的成绩,降序排序

  2. 方法分析

    (1)权限修饰符:public

    (2)是否需要static修饰?不需要

    (3)返回值类型:void

    (4)方法名:scoreSortDesc

    (5)形式参数列表:不需要参数

  3. 方法声明

    public void scoreSortDesc();

/**                                                                   
 * 按照成绩降序排序的算法                                                 
 */                                                                   
public void scoreSortDesc() {                                         
	/*                                                               
	 * 排序算法操作不能在原数据数组中进行直接操作,需要另外准备             
	 * 一个用于排序的临时数组,这里就涉及到一个数据拷贝过程。               
	 */                                                               
	Student[] sortTemp = new Student[size];                           

	for (int i = 0; i < sortTemp.length; i++) {                       
		sortTemp[i] = allStus[i];                                     
	}                                                                 

	for (int i = 0; i < sortTemp.length - 1; i++) {                   
		// 找出极值                                                   
		int index = i;                                               
		
        for (int j = i + 1; j < sortTemp.length; j++) {               
			// 按照成绩降序排序                                               
			if (sortTemp[index].getScore() > sortTemp[j].getScore()) {
				index = j;                                           
			}                                                         
		}                                                             
		                                                             
		// 交换位置                                                   
		if (index != i) {                                             
			Student stu = sortTemp[index];                           
			sortTemp[index] = sortTemp[i];                           
			sortTemp[i] = stu;                                       
		}                                                             
	}                                                                 
	                                                                 
	for (int i = 0; i < sortTemp.length; i++) {                       
		System.out.println(sortTemp[i]);                             
	}                                                                 
} 
1.7 主页面完成
package com.qfedu.student.system.mainproject;

import java.util.Scanner;

import com.qfedu.student.system.entity.Student;
import com.qfedu.student.system.manager.StudentManager;

public class MainProject {
	public static void main(String[] args) {
		StudentManager stm = new StudentManager();
	
		for (int i = 0; i < 15; i++) {
			Student student = new Student();
			
			student.setId(i + 1);
			student.setName("测试" + i);
			student.setGender(Math.random() > 0.5 ? '男' : '女');
			// 随机数
			student.setAge((int) (Math.random() * 100));
			student.setScore((int) (Math.random() * 100));
			
			stm.add(student);
		}
		
		Scanner sc = new Scanner(System.in);
		int choose = 0;
		int id = 0;
		
		while (choose != 7) {
			System.out.println("1. 添加新学生");
			System.out.println("2. 删除指定ID的学生");
			System.out.println("3. 修改指定ID的学生");
			System.out.println("4. 查询指定ID的学生");
			System.out.println("5. 查询所有学生");
			System.out.println("6. 按照成绩降序排序");
			System.out.println("7. 退出");
			
			choose = sc.nextInt();
			sc.nextLine();
			
			switch (choose) {
			case 1:
				/*
				 * 大家自己完成提供给用户一些输入提示,完成添加操作
				 */
				stm.add(new Student(100, "骚磊", 16, '男', 119));
				break;
			case 2:
				System.out.println("请输入需要删除学生的ID号");
				id = sc.nextInt();
				
				stm.remove(id);
				break;
			case 3:
				System.out.println("请输入需要修改信息的学生ID号");
				id = sc.nextInt();
				
				stm.modify(id);
				break;
			case 4:
				System.out.println("请输入需要查询信息的学生ID号");
				id = sc.nextInt();
				
				Student student = stm.get(id);
				if (null == student) {
					System.out.println("查无此人");
				} else {
					System.out.println(student);
				}
				break;
			case 5:
				stm.showAllStudents();
				break;
			case 6:
				stm.scoreSortDesc();
				break;
			case 7:
				System.out.println("程序退出...");
				break;

			default:
				System.out.println("选择错误");
				break;
			}
		}
	}
}
1.8 项目后期功能
  1. 数据持久化保存

    IO操作文件内容,String字符串解析

  2. Sout界面分离

    除了viewer层,其他任何代码中不能出现任何一个sout

  3. 排序算法优化

    可以让排序方法能够根据用户指定条件呢进行排序操作

  4. 自定义异常添加

    优化代码中错误提示和处理过程

  5. 数据根据条件过滤展示

    根据用户指定条件,过滤展示对应的数据

你可能感兴趣的:(java,编程语言)