导学
关于Java面向对象的知识已经学习过一部分了。在近期的学习中,我们遇到了很多的概念和知识点。在本章节内容中,我们就来通过一个简单的案例,运用Java加面向对象的思想,完成一个模拟场景的实现。
案例简介
在某一个学校开设了一个专业,叫做计算机科学与应用,专业编号J0001,学制为4年。
有四名学生报名学习了本专业,每个人的具体信息如下所示:
通过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;}//对于专业学习的时长不能低于0public 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 返回描述专业相关信息的字符串* 在真实项目中,对于信息的输出不会利用System.out.println()。* Java程序一般会提供信息出来,具体的信息输出方式由调用者决定*/public String info() {//字符串的拼接,尽量不要写成一长串的形式String str = "专业信息如下:\n专业名称:" + this.getSubjectName() + "\n";str += "专业编号:" + this.getSubjectNo() + "\n";str += "学制年限:" + this.getSubjectNo() + "年";return str;}}
测试类
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());}}
编写学生类及其测试
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*///在Java中两个等于号,只能用来判断基本数据类型是否相同//如果用来比较引用数据类型,比较的是两个引用数据类型在内存中地址是否相同,所以在Java中两个等于号不能判断字符串是否相等//判断字符串是否相等使用equals方法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;}}
测试:
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方法,添加专业信息, 直接传入所需参数
/*** 学生自我介绍 + 所学专业* @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;}
关联:
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方法,将专业对象作为参数整体传入
/*** 学生自我介绍 + 所学专业* @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;}
关联:
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));}}
通过对象传参,可以一次性的获取对象中的所有信息。比如我们可以在方法中获取对象的专业编号,这是之前方法在不改变参数的情况下无法做到的。
方案三:将专业对象作为成员属性存在
在进入大学的时候,每个人都需要先选择一个专业作为自己学习的方向,因此,我们可以将专业的信息作为学生的一个属性
package com.dodoke.school.model;public class Student {//成员属性:学号,姓名,性别,年龄 专业private String studentNo;private String studentName;private String studentSex;private int studentAge;//在进行程序设计的时候,如果使用引用数据类型作为属性,需要考虑该属性是否会被经常引用//如果使用频率高,需要考虑是否存在空指针的风险!!!//使用对象作为属性和使用基本数据类型作为属性没有区别-都是数据类型 + 属性名//注意:此时的sub对象没有被实例化,依旧是默认值nullprivate 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是否已经被实例化了//为了防止在未对sub属性进行赋值的时候就去取sub属性if(this.sub == null) {this.sub = new Subject();//如果在获取sub属性时,sub为null,直接进行sub属性的实例化}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;}}
测试:
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());//空指针异常}}
各方案小结
新增功能
新增需求及分析
对于这个专业,如果想要统计该专业有多少个学生进行了报名学习?
对于这个需求,我们可以试着通过将报名的学生装入一个容器中,然后统计该容器中有多少个学生就可以了。
那么该使用什么样的容器呢?
添加数组作为属性完成学生信息存储
package com.dodoke.school.model;/*** 专业类* @author LiXinRong**/public class Subject {//成员属性:学科名称、学科编号、学制年限、报名该专业的学习信息、报名该专业的学生个数private String subjectName;private String subjectNo;private int subjectLife;private Student[] students;//此时该数组为初始化,默认值为null//因为数组的长度200,并不一定是实际保存的学生个数,所以设置一个属性来统计学生个数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);//对于该方法中的两步操作,还可以怎么写?}}
测试:
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() + "个学生进行了报名");}}
问题分析
数组未实例化造成的空指针异常
实际上,在这样的一个小案例中,我们通常会发现学生会出现一些问题。
public Student[] getStudents() {/*if(this.students == null) {//对于数组动态初始化,没有办法准确的定下数组长度,只能根据具体情况具体分析this.students = new Student[200];}*/return students;}
如果将上述代码注释掉之后,我们会发现,运行时出现了空指针的异常。这是因为,我们没有针对数组进行初始化。解决方法有两种:
- 给数组属性直接初始化
-
通过一个方法完成学生和专业的双向关联
之前的代码中,我们先通过实例化学生完成了学生与专业之间的关联,而后又通过添加学生方法实现了,专业与学生之间的关联。是否能有一个办法,完成学生与专业的双向关联。

/*** 将学生信息保存到数组中,将学生个数保存到个数统计中* @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);//对于该方法中的两步操作,还可以怎么写?}
测试:
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() + "个学生进行了报名");}}
练习
一、选择
执行下面代码后,哪几个结论是正确的

A. f[0]B. f[0] = 0.0C. 编译失败D. 在运行时抛出异常
执行下面代码后,哪几个结论是正确的(多选)
String[ ] s = new String[10];
A. s[9]为nullB. s[10]的内容为空字符串C. 没有s[0]D. s.length=10
二、编程题
题目要求:
某公司要开发内部的 “办公信息化管理系统”,请使用面向对象的思想描述以下员工信息。


程序运行参考效果图如下:
任务描述
一、语言和环境
- 实现语言
Java语言 - 环境要求及开发工具
JDK、Eclipse
二、程序整体要求
- 划分功能模块,根据题目要求设置不同的类,在类中实现相应功能的管理。
- 类的标识要清楚易懂,代码结构要层次分明,代码编辑思路要清晰、整洁。
- 要求Java代码书写、命名符合规范,属性所属数据类型要准确合理,在代码中添加必要的注释
- 程序运行效果与提供的页面效果图、结构保持一致,信息间分隔符“=”号数量不做统一要求,文字大小、颜色也不做统一要求
- 作业完成后发表在自己的博客上
三、思路分析:
由场景和运行效果,可以分析出项目中可以抽取如下类(要求编码时注意面向对象思想及封装特性的应用):
- 部门类:
类型描述:能够描述部门编号、部门名称、员工数组、统计部门中的员工个数
要求:设定方法统计该部门员工个数
提示:部门类的属性有四个,分别是部门编号,部门名称,员工数组 和 统计变量 ,具体是市场部还是人事部,是通过部门类的对象来表示的,如果是市场部的对象,那么添加并统计的就是市场部的人数,同样如果是人事部的对象,添加并统计的就是人事部的人数 - 职务类:
类型描述:能够描述职务编号、职务名称 - 员工类:类型描述:能够描述员工姓名、工号、年龄、性别、所属部门、职务信息要求:
- 设定方法限定年龄只能是18—65之间,反之则设置默认为18岁
- 设定方法限定性别只能是“男”或者“女”,反之则设置默认为”男”
- 设定方法,实现员工自我介绍信息,将员工信息作为字符串返回
- 测试类:
类型描述:测试程序,并参照效果图输出结果
PS:注意:可以在属性上添加适当的信息验证,提高信息的安全性
