18_学生管理系统源码开发

1、项目目的
1.1 MVC设计模式
Model	模块
View	视图
Controller	控制器
1.2 主要业务逻辑
增	add
删	remove
改	modifyStudent
查	findStudent
2、项目主要功能
学生管理系统
	学生信息:
		学号、姓名、性别、Java成绩、数据库成绩、Web成绩、总分

	实现功能:
		1、添加新学生
		2、修改学生信息
		3、删除学生信息
		4、查询指定名称的学生
		5、查询所有学生信息
		6、根据成绩排序学生信息
3、项目分析
3.1 学生实体类实现和学号自动生成
ID自动生成:
	1、当前数据存在一定的持久性和共享特征 需要一个 static 关键字修饰的静态成员变量 用于存储赋值 id 的数据内容
	2、id 在实例化对象的过程中产生 使用构造代码块 给予所有实例化对象国策灰姑娘统一 id自增赋值操作
3.2 学生信息代码运行存储问题
分析:
	1、数据类型确定为 Student 学生类型
	2、数据个数不确定 每个班人数不固定
准备一个类 属于 Model 模块
	1、成员变量为学生类型的数组
	2、针对于学生类型的数组中的数据信息 完成增删改查操作
public class StudentModel {
	private Student[] arr;
}
3.3 StudentModel 底层数组容量和构造方法
分析:
	1、目前只定义了一个底层 Student 类型数组 需要进行数组容量初始化操作
	2、无参构造方法和有参构造方法
		构造方法给予数组容量初始化操作
		无参构造方法 直接使用【固定默认】的容量给予数组容量初始化操作
		有参构造方法 将底层数组的容量交给调用者决定 给予调用者参数信息 参数决定数组容量
//导入当前项目中其它package 内的类 这里导入的是 Student 实体类
import com.qfedu.stusys.entity.Student;

/**
 * 学生模块
 * 
 * @author Echo
 *
 */
public class StudentModel {
	/**
	 * 私有化成员变量 学生对象数组
	 * 一个数组为一个班级
	 * 【注意】
	 * 当前数组没有分配容量空间
	 */
	private Student[] allStudents;
	
	/**
	 * 私有化 static final 修饰成员变量 用于描述当前底层 Student 类型数组的默认容量
	 * 默认容量
	 */
	private static final int DEFAULT_CAPACITY = 10;
	
	/**
	 * 数组允许的最大容量 Integer.MAX_VALUE - 8
	 * 数组允许的最大容量
	 */
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	
	/**
	 * 底层数组容量 初始化为 0 
	 */
	private int size = 0;
	
	/**
	 * 无参数构造方法 初始化底层数组容量DEFAULT_CAPACITY 保证用户的基本使用
	 */
	public StudentModel() {
		allStudents = new Student[DEFAULT_CAPACITY];
	}
	/**
	 * 根据用户提供的初始化容量来决定当前底层数组的容量数据
	 * 
	 * @param initCapacity 用户提供的初始化容量
	 */
	public  StudentModel(int initCapacity) {
		if (initCapacity < 0 || initCapacity > MAX_ARRAY_SIZE) {
			// TODO 无法利用返回值操作提示用户错误信息 后期需要异常处理
		}
		
		allStudents = new Student[initCapacity];
	}
	
	/**
	 * 展示数组元素内容
	 */
	public void show() {
		for (int i = 0; i < size; i++) {
			System.out.println(allStudents[i]);
		}
	}
}
3.4 添加学生信息
/**
	 * 添加学生对象到当前 StudentModel 底层数组中
	 * @param student student对象
	 * @return 添加成功返回 true 否则返回 false
	 */
	public boolean add(Student student) {
		//底层数组添加学生 采用的方式为尾插法 size 是数组有效元素个数
		allStudents[size++] = student;
		
		return true;
	}
3.5 StudentModel 底层数组容量扩容操作
扩容方法流程
	1、获取原数组容量 oldCapacity
	2、计算得到新数组容量 newCapacity 是原数组容量的 1.5 倍左右
	3、判断新数组容量是否满足最小容量需求 minCapacity
	4、判断新数组容量是否在数组允许最大容量范围内 MAX_ARRAY_SIZE 即 Integer.MAX_VALUE - 8
	5、创建一个新数组
	6、将原数组中的数据移动到新数组
	7、保存新数组地址
/**
	 * 私有化成员方法 用于在添加操作中发现容量不足的情况 对底层数组进行扩容操作
	 * 所需参数是当前扩容最小容量需求
	 * @param minCapacity 最小容量
	 */
	private void grow(int minCapacity) {
		//1、获取原数组容量 
		int oldCapacity = allStudents.length;
		
		//2、计算得到的新数组 是原数组容量的 1.5 倍左右
		int newCapacity = oldCapacity + oldCapacity / 2;
		
		//3、判断新数组容量是否满足最小容量需求(miniCapacity)原数组数据个数 + 新添加数据个数
		if (newCapacity < minCapacity) {
			newCapacity = minCapacity;
		}
		
		//4、判断新数组容量是否超出数组允许的最大容量范围 是否大于 MAX_ARRAY_SIZE
		if (newCapacity > MAX_ARRAY_SIZE) {
			// TODO 通过异常处理操作 当前数组无法满足存储需求 
		}
		
		//5、创建一个新数组
		Student[] temp = new Student[newCapacity];
		
		//6、将原数组中的数据移动到新数组
		for (int i = 0; i < allStudents.length; i++) {
			temp[i] = allStudents[i];
		}
		
		//7、保存新数组地址
		allStudents = temp;
	}
3.6 删除指定 id 的学生信息
流程:
	1、根据指定 id 找到对应学生对象在数组中的下标位置
	2、如果下标不存在 终止删除操作 如果下标存在 进入删除操作流程
	3、删除流程
		3.1 定义一个临时局部变量保留学生对象
		3.2 从删除位置之后的数据 整体前移一位
		3.3 原本最后一个下标位置 赋值为 null 表示空
		3.4 有效元素个数减一 size -= 1
/**
	 * 根据 id 删除对应的学生对象 并返回被删除的对象
	 * @param id 被删除学生对象对应的 id
	 * @return 删除成功返回被删除的学生对象 否则返回 null
	 */
	public Student remove(int id) {
		int index = findIndexById(id);
		
		//如果 index 结果为 -1 当前指定 id 信息的学生不存在 返回 null
		if (-1 == index) {
			return null;
		}
		
		//删除操作
		//定义一个局部变量 存储被删除的元素
		Student student = allStudents[index];
		
		/*
		 * 使用循环从被删除下标位置开始 到最后一个有效元素下标位置 - 1结束 
		 * 数组中被删除的元素之后的元素整体前移一位
		 */
		for (int i = index; i < size - 1; i++) {
			allStudents[i] = allStudents[i + 1];
		}
		
		/*
		 * 数组中最后一个有效元素位置前移 原先的位置赋空值
		 * 有效元素个数 - 1
		 */
		allStudents[--size] = null; 
		
		return student;
	}

【补充方法】找出指定 id 对应的学生对象在数组中的下标位置

/**
	 * 根据学生对象的 id 找出学生对象在数组中对应的下标位置
	 * @param id 学生 id
	 * @return 找到后返回指定 id 对应的学生对象在数组中的下标位置 否则返回 -1
	 */
	private int findIndexById(int id) {
		//判断 id 是否在合法范围内
		if (id <= 0) {
			return -1;
		}
		
		int index = -1; 
		
		for (int i = 0; i < size; i++) {
			//获取学生类型数组中 下标为 i 的学生对象 和当前目标数据 id 对比
			if (allStudents[i].getId() == id) {
				index = i;
				break;
			}
		}
		
		return index;
	}
3.7 修改指定 id 对应的学生对象信息
流程:
	1、根据 id 找到对应的学生对象
	2、如果找到对应学生对象 展示数据提示信息 实例化 Scanner 扫描器对象 进行输入修改操作
	3、修改完成 退出修改
/**
	 * 修改学生对象
	 * 根据 id 修改对应学生对象
	 * @param id
	 */
	public void modifyStudent(int id) {
		int index = findIndexById(id);
		
		if (-1 == index) {
			return;
		}
		
		//使用局部变量存储学生对象信息 方便展示
		Student stu = allStudents[index];
		
		//退出标记变量
		boolean flag = true;
		//用于存储用户的选择
		int choose = 0;
		//扫描器
		Scanner sc = new Scanner(System.in);
		
		while(flag) {
			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("JavaScore:" + stu.getJavaScore());
			System.out.println("DbScore:" + stu.getDbScore());
			System.out.println("WebScore:" + stu.getWebScore());
			System.out.println("TotalScore:" + stu.getTotalScore());
			
			System.out.println("1. 修改学生姓名");
			System.out.println("2. 修改学生年龄");
			System.out.println("3. 修改学生性别");
			System.out.println("4. 修改学生Java成绩");
			System.out.println("5. 修改学生Database成绩");
			System.out.println("6. 修改学生Web成绩");
			System.out.println("7. 退出修改");
			
			System.out.println("----------------");
			System.out.println("请输入序号:");
			//存储用户输入的选项
			choose = sc.nextInt();
			
			//换行字符
			sc.nextLine();
			
			switch(choose) {
			case 1:
				System.out.println("请输入学生姓名:");
				//获取用户输入的姓名 String 类型
				String name = sc.nextLine();
				//修改姓名
				stu.setName(name);
				break;
			case 2:
				System.out.println("请输入学生年龄:");
				//获取用户输入的年龄 int 类型
				int age = sc.nextInt();
				
				//修改年龄
				if (checkAge(age)) {
					stu.setAge(age);
				}
				break;
			case 3:
				System.out.println("请输入学生性别:");
				//获取用户输入的性别 char 类型
				char gender = sc.nextLine().charAt(0);
	
				//修改性别
				if (checkGender(gender)) {
					stu.setGender(gender);				
				}
				break;
			case 4:
				System.out.println("请输入学生Java成绩:");
				//获取用户输入的Java成绩
				int javaScore = sc.nextInt();
				
				//修改javaScore成绩
				if (checkScore(javaScore)) {
					stu.setJavaScore(javaScore);
				}
				break;
			case 5:
				System.out.println("请输入学生Database成绩:");
				//获取用户输入的Database成绩
				int dbScore = sc.nextInt();
				
				//修改dbScore成绩
				if (checkScore(dbScore)) {
					stu.setDbScore(dbScore);
				}
				break;
			case 6:
				System.out.println("请输入学生Web成绩:");
				//获取用户输入的Web成绩
				int webScore = sc.nextInt();
				
				//修改webScore成绩
				if (checkScore(webScore)) {
					stu.setJavaScore(webScore);
				}
				break;
			case 7:
				flag = true;
				break;
			default:
				System.out.println("输入错误");
				break;
			}
			
			//结束 while 循环
			if (flag) {
				break;
			}
		}
		sc.close();
	}

【补充方法】用户输入合法性判断

/**
	 * 用户输入成绩合法性判断
	 * @param score 用户提供的成绩分数
	 * @return 如果在合法范围内返回 true 否则返回 false
	 */
	private boolean checkScore(int score) {
		return score >= 0 && score <= 100;
	}
	
	/**
	 * 用户输入性别合法性判断
	 * @param gender 用户提供的性别
	 * @return 如果合法返回 true 否则返回 false
	 */
	private boolean checkGender(char gender) {
		return '男' == gender || '女' == gender;
	}
	
	/**
	 * 用户输入年龄合法性判断
	 * @param age 用户提供的年龄
	 * @return 如果合法返回 true 否则返回 false
	 */
	private boolean checkAge(int age) {
		return age >= 0 && age <= 120;
	}
3.8 根据指定 id 查询对应学生对象
/**
	 * 根据学生对象 id 信息 查询学生对象详情
	 * @param id 指定学生 id
	 * @return 找到后返回该 id 对应的学生对象 否则返回 null
	 */
	public Student findStudent(int id) {
		int index = findIndexById(id);
		
		return index != -1 ? allStudents[index] : null;
	}