企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
# 学生信息管理 [TOC] ## 导学 关于Java面向对象的知识已经学习过一部分了。在近期的学习中,我们遇到了很多的概念和知识点。在本章节内容中,我们就来通过一个简单的案例,运用Java加面向对象的思想,完成一个模拟场景的实现。 ## 案例简介 在某一个学校开设了一个专业,叫做计算机科学与应用,专业编号J0001,学制为4年。 有四名学生报名学习了本专业,每个人的具体信息如下所示: ![](https://img.kancloud.cn/36/1d/361d80f44417aa07b741c5ba1cb01901_568x258.png) 通过Java面向对象的语言来实现这样的场景,最后输出的内容如下所示: ![](https://img.kancloud.cn/21/95/21957fab788411419b543f89df6abda6_1016x495.png) ## 案例分析与实现 通过对该场景的分析,我们可以得到这样的两个类 ![](https://img.kancloud.cn/1b/0f/1b0faa8150a8a55ad70b56e3472aa02b_994x390.png) ### 编写专业类及其测试类 ~~~java package com.dodoke.school.model; /** * 专业类 * @author LiXinRong * */ public class Subject { //成员属性:学科名称、学科编号、学制年限 private String subjectName; private String subjectNo; private int subjectLife; public String getSubjectName() { return subjectName; } public void setSubjectName(String subjectName) { this.subjectName = subjectName; } public String getSubjectNo() { return subjectNo; } public void setSubjectNo(String subjectNo) { this.subjectNo = subjectNo; } public int getSubjectLife() { return subjectLife; } public void setSubjectLife(int subjectLife) { if(subjectLife < 0) { return;//直接使用return退出该setSubjectLife方法,使学制年限保存默认值为0 } this.subjectLife = subjectLife; } public Subject() { } public Subject(String subjectName, String subjectNo, int subjectLife) { super(); this.setSubjectLife(subjectLife); this.setSubjectName(subjectName); this.setSubjectNo(subjectNo); } /** * 该方法用于描述专业信息 * @return 返回描述专业相关信息的字符串 */ public String info() { //字符串的拼接,尽量不要写成一长串的形式,会让人看得难受 String str = "专业信息如下:\n专业名称:" + this.getSubjectName() + "\n"; str += "专业编号:" + this.getSubjectNo() + "\n"; str += "学制年限:" + this.getSubjectNo() + "年"; return str; } } ~~~ 测试类 ~~~java package com.dodoke.school.test; import com.dodoke.school.model.Subject; public class SchoolTest { public static void main(String[] args) { Subject sub = new Subject("计算机科学与应用","J0001",4); //info()方法为什么不直接打印输出:我们现在学习的语言环境是比较简陋的,只用在控制台打印输出信息 //如果需要在网页,在app中输出信息,则返回值为String类型的info()方法,可复用性更高一些 System.out.println(sub.info()); } } ~~~ ### 编写学生类及其测试 ~~~java package com.dodoke.school.model; public class Student { //成员属性:学号,姓名,性别,年龄 private String studentNo; private String studentName; private String studentSex; private int studentAge; public String getStudentNo() { return studentNo; } public void setStudentNo(String studentNo) { this.studentNo = studentNo; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } public String getStudentSex() { return studentSex; } /** * 性别限制为男或女,反之,强制限制为男 * @param studentSex */ public void setStudentSex(String studentSex) { if(studentSex.equals("男") || studentSex.equals("女")) { this.studentSex = studentSex; } else { this.studentSex = "男"; } } public int getStudentAge() { return studentAge; } /** * 给年龄赋值,限制范围在10-100之间,赋值设置为18岁 * @param studentAge */ public void setStudentAge(int studentAge) { if(studentAge > 10 && studentAge < 100) { this.studentAge = studentAge; } else { this.studentAge = 18; } } //无参构造 public Student() { super(); } //多参构造,实现对全部属性的赋值 public Student(String studentNo, String studentName, String studentSex, int studentAge) { super(); this.setStudentNo(studentNo); this.setStudentName(studentName); this.setStudentSex(studentSex); this.setStudentAge(studentAge); } /** * 学生自我介绍 * @return 用于返回描述学生信息字符串 */ public String introduction() { String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n"; str += "学号:" + this.getStudentNo()+ "\n"; str += "性别:" + this.getStudentSex()+ "\n"; str += "年龄:" + this.getStudentAge(); return str; } } ~~~ 测试: ~~~java public class SchoolTest { public static void main(String[] args) { Subject sub = new Subject("计算机科学与应用","J0001",4); //info()方法为什么不直接打印输出:我们现在学习的语言环境是比较简陋的,只用在控制台打印输出信息 //如果需要在网页,在app中输出信息,则返回值为String类型的info()方法,可复用性更高一些 System.out.println(sub.info()); System.out.println("============================="); Student stu0 = new Student("S01","张三","男", 18); System.out.println(stu0.introduction()); } } ~~~ ### 专业与学生关联起来 #### 方案一:重载introduction方法,添加专业信息 ~~~java /** * 学生自我介绍 + 所学专业 * @param subjectName 所学专业名称 * @param subjectLife 学制年限 * @return 用于返回描述学生信息和所学专业的字符串 */ public String introduction(String subjectName, int subjectLife) { String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n"; str += "学号:" + this.getStudentNo()+ "\n"; str += "性别:" + this.getStudentSex()+ "\n"; str += "年龄:" + this.getStudentAge() + "\n"; str += "所报专业名称:" + subjectName + "\n"; str += "学制年限:" + subjectLife + "\n"; return str; } ~~~ 关联: ~~~java public static void main(String[] args) { Subject sub = new Subject("计算机科学与应用","J0001",4); System.out.println(sub.info()); System.out.println("============================="); Student stu0 = new Student("S01","张三","男", 18); System.out.println(stu0.introduction()); System.out.println("============================="); Student stu1 = new Student("S02","李四","女", 19); System.out.println(stu1.introduction("计算机科学与应用", 4)); } ~~~ #### 方案二:重载introduction方法,将专业对象作为参数 ~~~java /** * 学生自我介绍 + 所学专业 * @param sub 专业对象 * @return 用于返回描述学生信息和所学专业的字符串 */ public String introduction(Subject sub) { String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n"; str += "学号:" + this.getStudentNo()+ "\n"; str += "性别:" + this.getStudentSex()+ "\n"; str += "年龄:" + this.getStudentAge() + "\n"; str += "所报专业名称:" + sub.getSubjectName() + "\n"; str += "学制年限:" + sub.getSubjectLife() + "\n"; return str; } ~~~ 关联: ~~~java public class SchoolTest { public static void main(String[] args) { Subject sub = new Subject("计算机科学与应用","J0001",4); System.out.println(sub.info()); System.out.println("============================="); Student stu0 = new Student("S01","张三","男", 18); System.out.println(stu0.introduction()); System.out.println("============================="); Student stu1 = new Student("S02","李四","女", 19); System.out.println(stu1.introduction("计算机科学与应用", 4)); System.out.println("============================="); Student stu2 = new Student("S03","王五","男", 18); System.out.println(stu2.introduction(sub)); } } ~~~ 通过对象传参,可以一次性的获取对象中的所有信息。比如我们可以在方法中获取对象的专业编号,这是之前两个方法在不改变参数的情况下无法做到的。 #### 方案三:将专业对象作为成员属性存在 在进入大学的时候,每个人都需要先选择一个专业作为自己学习的方向,因此,我们可以将专业的信息作为学生的一个属性 ~~~java package com.dodoke.school.model; public class Student { //成员属性:学号,姓名,性别,年龄 专业 private String studentNo; private String studentName; private String studentSex; private int studentAge; //使用对象作为属性和使用基本数据类型作为属性没有区别-都是数据类型 + 属性名 //注意:此时的sub对象没有被实例化,依旧是默认值null private Subject sub; public String getStudentNo() { return studentNo; } public void setStudentNo(String studentNo) { this.studentNo = studentNo; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } public String getStudentSex() { return studentSex; } /** * 性别限制为男或女,反之,强制限制为男 * @param studentSex */ public void setStudentSex(String studentSex) { if(studentSex.equals("男") || studentSex.equals("女")) { this.studentSex = studentSex; } else { this.studentSex = "男"; } } public int getStudentAge() { return studentAge; } /** * 给年龄赋值,限制范围在10-100之间,赋值设置为18岁 * @param studentAge */ public void setStudentAge(int studentAge) { if(studentAge > 10 && studentAge < 100) { this.studentAge = studentAge; } else { this.studentAge = 18; } } /** * 获取专业对象,如果没有实例化,先实例化再返回 * @return */ public Subject getSub() { //为安全起见,在返回时,判断sub是否已经被实例化了 if(this.sub == null) { this.sub = new Subject(); } return sub; } public void setSub(Subject sub) { //此处就是通过外部参数对sub属性进行实例化 this.sub = sub; } //无参构造 public Student() { super(); } //多参构造 public Student(String studentNo, String studentName, String studentSex, int studentAge) { super(); this.setStudentNo(studentNo); this.setStudentName(studentName); this.setStudentSex(studentSex); this.setStudentAge(studentAge); } //多参构造,实现对全部属性的赋值 public Student(String studentNo, String studentName, String studentSex, int studentAge, Subject sub) { super(); this.setStudentNo(studentNo); this.setStudentName(studentName); this.setStudentSex(studentSex); this.setStudentAge(studentAge); this.setSub(sub); } /** * 学生自我介绍 * @return 用于返回描述学生信息字符串 */ public String introduction() { String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n"; str += "学号:" + this.getStudentNo()+ "\n"; str += "性别:" + this.getStudentSex()+ "\n"; str += "年龄:" + this.getStudentAge() + "\n"; str += "所报专业名称:" + this.sub.getSubjectName() + "\n"; str += "学制年限:" + this.sub.getSubjectLife() + "\n"; return str; } /** * 学生自我介绍 + 所学专业 * @param subjectName 所学专业名称 * @param subjectLife 学制年限 * @return 用于返回描述学生信息和所学专业的字符串 */ public String introduction(String subjectName, int subjectLife) { String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n"; str += "学号:" + this.getStudentNo()+ "\n"; str += "性别:" + this.getStudentSex()+ "\n"; str += "年龄:" + this.getStudentAge() + "\n"; str += "所报专业名称:" + subjectName + "\n"; str += "学制年限:" + subjectLife + "\n"; return str; } /** * 学生自我介绍 + 所学专业 * @param sub 专业对象 * @return 用于返回描述学生信息和所学专业的字符串 */ public String introduction(Subject sub) { String str = "学生信息如下:\n姓名:" + this.getStudentName() + "\n"; str += "学号:" + this.getStudentNo()+ "\n"; str += "性别:" + this.getStudentSex()+ "\n"; str += "年龄:" + this.getStudentAge() + "\n"; str += "所报专业名称:" + sub.getSubjectName() + "\n"; str += "学制年限:" + sub.getSubjectLife() + "\n"; return str; } } ~~~ 测试: ~~~java public class SchoolTest { public static void main(String[] args) { Subject sub = new Subject("计算机科学与应用","J0001",4); System.out.println(sub.info()); System.out.println("============================="); //Student stu0 = new Student("S01","张三","男", 18); //System.out.println(stu0.introduction());空指针异常 Student stu0 = new Student("S01","张三","男", 18, sub); System.out.println(stu0.introduction());//空指针异常 } } ~~~ #### 各方案小结 ![](https://img.kancloud.cn/e4/31/e431ff0af4391c236b2b622ffdf49476_954x557.png) ## 新增功能 ### 新增需求及分析 对于这个专业,如果想要统计该专业有多少个学生进行了报名学习? ![](https://img.kancloud.cn/82/ec/82ecf63ae54b03b0da43d2897f84ed66_714x502.png) 对于这个需求,我们可以试着通过将报名的学生装入一个容器中,然后统计该容器中有多少个学生就可以了。 ![](https://img.kancloud.cn/bd/92/bd92f24b012e08d1eb40d65fe4d3d998_387x373.png) 那么该使用什么样的容器呢? ### 添加数组作为属性完成学生信息存储 ~~~java package com.dodoke.school.model; /** * 专业类 * @author LiXinRong * */ public class Subject { //成员属性:学科名称、学科编号、学制年限、报名该专业的学习信息、报名该专业的学生个数 private String subjectName; private String subjectNo; private int subjectLife; private Student[] students;//此时该数组为初始化,默认值为null //因为数组的长度200,实际保存学生30,并不一定是实际保存的学生个数,所以设置一个属性来统计学生个数 private int studentNum; public String getSubjectName() { return subjectName; } public void setSubjectName(String subjectName) { this.subjectName = subjectName; } public String getSubjectNo() { return subjectNo; } public void setSubjectNo(String subjectNo) { this.subjectNo = subjectNo; } public int getSubjectLife() { return subjectLife; } public void setSubjectLife(int subjectLife) { if(subjectLife < 0) { return;//直接使用return退出该setSubjectLife方法,使学制年限保存默认值为0 } this.subjectLife = subjectLife; } /** * 获取选修专业的学生信息,如果保存的学生信息的数组未初始化,则,先初始化长度200 * @return 保存学生信息的数组 */ public Student[] getStudents() { if(this.students == null) { //对于数组动态初始化,没有办法准确的定下数组长度,只能根据具体情况具体分析 this.students = new Student[200]; } return students; } public void setStudents(Student[] students) { this.students = students; } public int getStudentNum() { return studentNum; } public void setStudentNum(int studentNum) { if(studentNum < 0) { this.studentNum = 0; return;//这里使用同样使用return终结方法的运行 } this.studentNum = studentNum; } public Subject() { } //带参构造 public Subject(String subjectName, String subjectNo, int subjectLife) { super(); this.setSubjectLife(subjectLife); this.setSubjectName(subjectName); this.setSubjectNo(subjectNo); } public Subject(String subjectName, String subjectNo, int subjectLife, Student[] stus) { super(); this.setSubjectLife(subjectLife); this.setSubjectName(subjectName); this.setSubjectNo(subjectNo); this.setStudents(stus); } /** * 该方法用于描述专业信息 * @return */ public String info() { //字符串的拼接,尽量不要写的太长,会让人看得难受 String str = "专业信息如下:\n专业名称:" + this.getSubjectName() + "\n"; str += "专业编号:" + this.getSubjectNo() + "\n"; str += "学制年限:" + this.getSubjectLife() + "年"; return str; } /** * 将学生信息保存到数组中,将学生个数保存到个数统计中 * @param stu 学生信息 */ public void addStudent(Student stu) { /* 如何添加学生信息到数组中去 * 需要将学生数组进行遍历,依次判读数组中保存的元素是否为null * 如果为null就可以用学生信息(学生对象)替代 */ int i; for(i = 0; i < this.getStudents().length; i++) { if(this.getStudents()[i] == null) { this.getStudents()[i] = stu; break; } } //将个数保存到个数统计中去 this.setStudentNum(i + 1); //对于该方法中的两步操作,还可以怎么写? } } ~~~ 测试: ~~~java public class SchoolTest { public static void main(String[] args) { Subject sub = new Subject("计算机科学与应用","J0001",4); System.out.println(sub.info()); System.out.println("============================="); Student stu0 = new Student("S01","张三","男", 18, sub); System.out.println(stu0.introduction()); System.out.println("============================="); Student stu1 = new Student("S02","李四","女", 19); System.out.println(stu1.introduction("计算机科学与应用", 4)); System.out.println("============================="); Student stu2 = new Student("S03","王五","男", 18); System.out.println(stu2.introduction(sub)); System.out.println("============================="); sub.addStudent(stu0); sub.addStudent(stu1); sub.addStudent(stu2); System.out.println(sub.getSubjectName() + "的专业中,已有" + sub.getStudentNum() + "个学生进行了报名"); } } ~~~ ### 问题分析 #### 数组未实例化造成的空指针异常 实际上,在这样的一个小案例中,我们通常会发现学生会出现一些问题。 ~~~java public Student[] getStudents() { /*if(this.students == null) { //对于数组动态初始化,没有办法准确的定下数组长度,只能根据具体情况具体分析 this.students = new Student[200]; }*/ return students; } ~~~ 如果将上述代码注释掉之后,我们会发现,运行时出现了空指针的异常。这是因为,我们没有针对数组进行初始化。解决方法有两种: 1. 给数组属性直接初始化 2. 在使用数组属性时初始化 #### 通过一个方法完成学生和专业的双向关联 之前的代码中,我们先通过实例化学生完成了学生与专业之间的关联,而后又通过添加学生方法实现了,专业与学生之间的关联。是否能有一个办法,完成学生与专业的双向关联。 ![](https://img.kancloud.cn/47/13/471354b70afb4e0668f88570c8bc0392_643x212.png) ~~~java /** * 将学生信息保存到数组中,将学生个数保存到个数统计中 * @param stu 学生信息 */ public void addStudent(Student stu) { /* 如何添加学生信息到数组中去 * 需要将学生数组进行遍历,依次判读数组中保存的元素是否为null * 如果为null就可以用学生信息(学生对象)替代 */ int i; for(i = 0; i < this.getStudents().length; i++) { if(this.getStudents()[i] == null) { stu.setSub(this); this.getStudents()[i] = stu; break; } } //将个数保存到个数统计中去 this.setStudentNum(i + 1); //对于该方法中的两步操作,还可以怎么写? } ~~~ 测试: ~~~java public class SchoolTest { public static void main(String[] args) { Subject sub = new Subject("计算机科学与应用","J0001",4); Student stu0 = new Student("S01","张三","男", 18); sub.addStudent(stu0); System.out.println(sub.getSubjectName() + "的专业中,已有" + sub.getStudentNum() + "个学生进行了报名"); } } ~~~ ## 练习 一、选择 1. 执行下面代码后,哪几个结论是正确的 ![](https://img.kancloud.cn/53/ae/53aecb6d421e14f962fc1ec264d16a03_453x127.png) ~~~ A. f[0] B. f[0] = 0.0 C. 编译失败 D. 在运行时抛出异常 ~~~ 2. 执行下面代码后,哪几个结论是正确的(多选) ~~~ String[ ] s = new String[10]; ~~~ ~~~ A. s[9]为null B. s[10]的内容为空字符串 C. 没有s[0] D. s.length=10 ~~~ 二、编程题 题目要求: 某公司要开发内部的 “办公信息化管理系统”,请使用面向对象的思想描述以下员工信息。 ![](https://img.kancloud.cn/af/3d/af3df74bae0d0d02818c3e5e3009e76b_580x329.png) ![](https://img.kancloud.cn/c2/da/c2dac230414f7cadde6b4af56e9c6cec_631x146.png) ![](https://img.kancloud.cn/73/de/73de33ba55ed27484becc996f60ec5b3_320x192.png) 程序运行参考效果图如下: ![](https://img.kancloud.cn/ae/38/ae38d91e08e0a8e50e360f110feb39eb_303x659.png) **任务描述** 一、语言和环境 * 实现语言 Java语言 * 环境要求及开发工具 JDK、Eclipse 二、程序整体要求 1. 划分功能模块,根据题目要求设置不同的类,在类中实现相应功能的管理。 2. 类的标识要清楚易懂,代码结构要层次分明,代码编辑思路要清晰、整洁。 3. 要求Java代码书写、命名符合规范,属性所属数据类型要准确合理,在代码中添加必要的注释 4. 程序运行效果与提供的页面效果图、结构保持一致,信息间分隔符“=”号数量不做统一要求,文字大小、颜色也不做统一要求 5. 作业完成后发表在自己的博客上 三、思路分析: 由场景和运行效果,可以分析出项目中可以抽取如下类(要求编码时注意面向对象思想及封装特性的应用): * 部门类: 类型描述:能够描述部门编号、部门名称、员工数组、统计部门中的员工个数 要求:设定方法统计该部门员工个数 提示:部门类的属性有四个,分别是部门编号,部门名称,员工数组 和 统计变量 ,具体是市场部还是人事部,是通过部门类的对象来表示的,如果是市场部的对象,那么添加并统计的就是市场部的人数,同样如果是人事部的对象,添加并统计的就是人事部的人数 * 职务类:  类型描述:能够描述职务编号、职务名称 * 员工类: 类型描述:能够描述员工姓名、工号、年龄、性别、所属部门、职务信息 要求: * 设定方法限定年龄只能是18--65之间,反之则设置默认为18岁 * 设定方法限定性别只能是“男”或者“女”,反之则设置默认为"男" * 设定方法,实现员工自我介绍信息,将员工信息作为字符串返回 * 测试类: 类型描述:测试程序,并参照效果图输出结果 PS:注意:可以在属性上添加适当的信息验证,提高信息的安全性