# 學生信息管理
[TOC]
## 導學
關于Java面向對象的知識已經學習過一部分了。在近期的學習中,我們遇到了很多的概念和知識點。在本章節內容中,我們就來通過一個簡單的案例,運用Java加面向對象的思想,完成一個模擬場景的實現。
## 案例簡介
在某一個學校開設了一個專業,叫做計算機科學與應用,專業編號J0001,學制為4年。
有四名學生報名學習了本專業,每個人的具體信息如下所示:

通過Java面向對象的語言來實現這樣的場景,最后輸出的內容如下所示:

## 案例分析與實現
通過對該場景的分析,我們可以得到這樣的兩個類

### 編寫專業類及其測試類
~~~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());//空指針異常
}
}
~~~
#### 各方案小結

## 新增功能
### 新增需求及分析
對于這個專業,如果想要統計該專業有多少個學生進行了報名學習?

對于這個需求,我們可以試著通過將報名的學生裝入一個容器中,然后統計該容器中有多少個學生就可以了。

那么該使用什么樣的容器呢?
### 添加數組作為屬性完成學生信息存儲
~~~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. 在使用數組屬性時初始化
#### 通過一個方法完成學生和專業的雙向關聯
之前的代碼中,我們先通過實例化學生完成了學生與專業之間的關聯,而后又通過添加學生方法實現了,專業與學生之間的關聯。是否能有一個辦法,完成學生與專業的雙向關聯。

~~~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. 執行下面代碼后,哪幾個結論是正確的

~~~
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
~~~
二、編程題
題目要求:
某公司要開發內部的 “辦公信息化管理系統”,請使用面向對象的思想描述以下員工信息。



程序運行參考效果圖如下:

**任務描述**
一、語言和環境
* 實現語言
Java語言
* 環境要求及開發工具
JDK、Eclipse
二、程序整體要求
1. 劃分功能模塊,根據題目要求設置不同的類,在類中實現相應功能的管理。
2. 類的標識要清楚易懂,代碼結構要層次分明,代碼編輯思路要清晰、整潔。
3. 要求Java代碼書寫、命名符合規范,屬性所屬數據類型要準確合理,在代碼中添加必要的注釋
4. 程序運行效果與提供的頁面效果圖、結構保持一致,信息間分隔符“=”號數量不做統一要求,文字大小、顏色也不做統一要求
5. 作業完成后發表在自己的博客上
三、思路分析:
由場景和運行效果,可以分析出項目中可以抽取如下類(要求編碼時注意面向對象思想及封裝特性的應用):
* 部門類:
類型描述:能夠描述部門編號、部門名稱、員工數組、統計部門中的員工個數
要求:設定方法統計該部門員工個數
提示:部門類的屬性有四個,分別是部門編號,部門名稱,員工數組?和?統計變量?,具體是市場部還是人事部,是通過部門類的對象來表示的,如果是市場部的對象,那么添加并統計的就是市場部的人數,同樣如果是人事部的對象,添加并統計的就是人事部的人數
* 職務類:
?類型描述:能夠描述職務編號、職務名稱
* 員工類:
類型描述:能夠描述員工姓名、工號、年齡、性別、所屬部門、職務信息
要求:
* 設定方法限定年齡只能是18--65之間,反之則設置默認為18歲
* 設定方法限定性別只能是“男”或者“女”,反之則設置默認為"男"
* 設定方法,實現員工自我介紹信息,將員工信息作為字符串返回
* 測試類:
類型描述:測試程序,并參照效果圖輸出結果
PS:注意:可以在屬性上添加適當的信息驗證,提高信息的安全性