最近这几天都在跟着做 “开发团队调度系统” 的项目,对面向对象的编程思想有了更加深入的理解,收获很多。
这个系统的主要任务是:从已经给定的公司员工的列表中,选择一定数目(设定的是5人)的员工,加入到 软件开发团队中。
其中,公司的员工股分为不同的种类:employee(普通员工)、programmer(程序员)、designer(设计师)、architect(架构师)。
对于要加入 软件开发团队 中的人,除了总人数设置的有限制之外,还要求:最多有3个程序员(programmer),最多有2个设计师(designer),最多有1个架构师(architect),并且不能有普通员工(employee)。
所以,在向 软件开发团队 中添加公司员工的时候,实际上是有诸多限制的。并不是简单地加进去就可以。
下面就对这个系统的主要设计思想、实现方法,进行简要概述:
软件设计结构
1、该软件主要由以下三个模块组成:
2、关于domain的设计:
说明:
(1)公司的员工类之间为继承关系,包含employee(普通员工)、programmer(程序员)、designer(设计师)、architect(架构师)这4类,对应的继承关系如图所示。
(2)公司为员工提供的设备,它们之间的关系并不是继承,但是却拥有一些相同或相似的特点,所以将它们所拥有的相似的特点,抽象出来,作为接口equipment,而公司为员工提供的设备:PC、NoteBook、Printer,则作为接口equipment的实现类。他们之间的实现关系也如上面的图所示。
数据的存储模式
在这里没有采用数据库,而是使用单一的数组来进行数据的暂存。具体的实现方式如下:
(1)将员工employee,和设备equipment,分别作为两个数组来进行存储。这里进行数据存储的数组,用的是string类型的二维数组:[number][],即外围数组的长度是确定的,内存数组的元素是不定的,可以根据存储的数据对应的员工类型的不同,分别确定它们的长度。
(2)最后,整理上面的两个数组元素成为一个数组,并且采用一个employee类的、一维的、对象数组,来进行存储至于每个数组元素所指向的对象的类型,取决于该位置上 对应的员工 的类型。
(3)由于 公司所有员工的数据 最终是存储在一个 一维的对象数组中的,所以 开发团队的员工信息,也应该存储在一个employee类的、一维的、对象数组中。由于限制条件“软件开发团队的成员,至少应该是程序员”,所以,该数组可以定义为programmer类型的 对象数组。
注意:
需要注意的是,对于公司的员工列表中,员工的状态这一项的设计,由于规定的只有free,busy,vocation三个选项,所以可以采取如下方式进行处理:模仿单例模式的方式:定义一个类status,在其中设置属性name,来标记status的值。然后构造三个静态的变量(对象),它们的name值分别为free,busy,vocation ,并在构造器中对他们的name属性进行赋值。之后,将employee数组中的对象的status属性初始化为free,在之后每进行一次状态转换,就对该对象的status属性进行一次手动赋值(状态切换)。
系统的主要代码实现
主要的几个类的代码(包含注释),如下:
1、NameListService
import com.athanchang.team.domain.Architect;
import com.athanchang.team.domain.Designer;
/**
*
* @Description 功能:将Data中的数据封装到Employee[]数组中,
* 同时提供相关操作Employee[]的方法
*/
import static com.athanchang.team.service.Data.*;
import com.athanchang.team.domain.Employee;
import com.athanchang.team.domain.Equipment;
import com.athanchang.team.domain.NoteBook;
import com.athanchang.team.domain.PC;
import com.athanchang.team.domain.Printer;
import com.athanchang.team.domain.Programmer;
public class NameListService {
// private static final String[][] EMPLOYEES = null;
/*
* 声明属性employees:用来保存公司所有员工的对象
* ------即,展示的数据都放在数组employees当中
*/
private Employee[] employees;//是一个属性 数组
/**
* 将employees数组填充:
* 在NameListService构造器中:
* 1、根据项目提供的Data类构建相应大小的employees数组
* 2、再根据Data类中的数据构建不同的对象,
* 包括Employee、Programmer、Designer和Architect
* 3、将对象存于数组中
*/
//构造器主要作用:对employees数组,以及数组元素做初始化
public NameListService() {
super();
//初始化数组
employees = new Employee[Data.EMPLOYEES.length];
/*
* 取决于EMPLOYEES的整个数组,
* 从头到尾,根据人家是什么类型的,
* 再来造对象
*/
//因为是用foe循环;所以,相当于将data类中的所有数据都加入到employees数组中来了
for(int i=0;i<employees.length;i++) {
/*
* EMPLOYEES[i][0]是string类型
* 而EMPLOYEE = 10,是int类型
* ------需要将string转化为int
*/
//获取员工的类型
int type = Integer.parseInt(EMPLOYEES[i][0]);
//获取员工的4个基本信息
int id = Integer.parseInt(Data.EMPLOYEES[i][1]);//因为id本来是string类型
String name = EMPLOYEES[i][2];
int age = Integer.parseInt(EMPLOYEES[i][3]);
double salary = Double.parseDouble(EMPLOYEES[i][4]);
/*
* 在这里只声明这个变量,不去获取
* 因为在这里获取的话,会存在风险:
* 例如“马云”,就不存在有设备的情况,
* 而相应的createEquipment(int index)中并没有考虑到没有设备的情况怎么处理
* 所以在这里,只做声明,不做处理
* ------解决了变量重名的问题
*/
Equipment equipment;
double bonus;
int stock;
switch(type) {
case Data.EMPLOYEE://10
employees[i] = new Employee(id, name, age, salary);
break;
case Data.PROGRAMMER://11
/*
* 需要将数组:EQUIPMENTS中的元素转化为对象
* ------
* 对象又分为好几种
* 因为 设备里边有几个具体的实现类
* EQUIPMENTS数组的每一列的第一个,不同的字符串表示是不同的设备
* ------把它封装到一个方法里
*/
equipment = createEquipment(i);
//i告诉是哪一个的索引位置,对应EQUIPMENTS数组中EQUIPMENTS[i]
employees[i] = new Programmer(id, name, age, salary, equipment);
break;
case Data.DESINGER://12
equipment = createEquipment(i);
bonus = Double.parseDouble(EMPLOYEES[i][5]);
employees[i] = new Designer(id, name, age, salary, equipment, bonus);
break;
case Data.ARCHITECT://13
equipment = createEquipment(i);
bonus = Double.parseDouble(EMPLOYEES[i][5]);
stock = Integer.parseInt(EMPLOYEES[i][6]);
employees[i] = new Architect(id, name, age, salary, equipment, bonus, stock);
}
}
}
/**
* @ Description 查找第i个位置的员工对应的设备的方法
* ------获取指定index上的员工的设备
* ----与处理 员工类型 的逻辑有点像
*/
private Equipment createEquipment(int index) {
int key = Integer.parseInt(Data.EQUIPMENTS[index][0]);
/*
* 第一个参数都是在外边统一获取的
* 第二个参数是自己独立获取的
*/
String modelOrName = Data.EQUIPMENTS[index][1];
switch(key) {
/*
* 注意:细节之处:
* 这里,一般case的情况并不直接使用 21、22、23
* 可读性非常差,并且改动起来很麻烦
* 一般是使用这里的 这种全局常量(static final)来进行标志,更清晰
*/
case Data.PC://21
/*
* 要返回一个PC对象
* model、display
*/
String display = Data.EQUIPMENTS[index][2];
return new PC(modelOrName, display);
case Data.NOTEBOOK://22
/*
* 要返回一个NOTEBOOK对象
* model、price
*/
double price = Double.parseDouble(Data.EQUIPMENTS[index][2]);
return new NoteBook(modelOrName, price);
case Data.PRINTER://23
String type = Data.EQUIPMENTS[index][2];
return new Printer(modelOrName, type);
}
return null;
}
/**
* @ Description 获取当前所有员工
* 返回,包含所有员工数组的对象
* -------
* 因为当前NameListService 类的属性数组
* Employee[] emploees
* 是私有的,
* 这个方法就相当于它的方法
*/
public Employee[] getAllEmployees() {
/*
*
* 因为是相当于get方法
* ------把员工数组返回一下就行
*/
return employees;
}
/**
* @ Description:
* 返回某一个具体的id,对应的employee
* ------
* 获取指定ID的员工对象
* 参数:指定员工的ID
* 返回:指定员工对象
* 异常:找不到指定的员工
*/
public Employee getEmployee(int id) throws TeamException {
/*
* 先在数组找一下,有没有要查找的index对应的员工
* ------遍历
*/
for(int i=0;i<employees.length;i++) {
if(employees[i].getId() == id) {
/*
* 这里的id如果是int型----==
* 如果是Integer型的话,
* 如果用 == ,对的话,是因为:
* integer内部定义的integercachae内部类,保存了-128~+127
* 是恰好撞上了;
* 正常来讲,就应该用equals(id是integer类型的话)
*是因为:凡是比较对象,两个对象的数据一样的话,用的都是equals
* 对象的时候,==比较的是指向同一个引用
*/
return employees[i];
}
}
/*
* 没找到的话,就抛出异常
*/
throw new TeamException("找不到指定的员工");
/*
* 先暂时抛出,
* 在最后整体调用的时候再try-catch
* 进行真正的处理异常
*/
}
}
2、TeamService类
/**
* @Description 开发团队的管理,添加、删除
* @author admin
*
*/
import com.athanchang.team.domain.Architect;
import com.athanchang.team.domain.Designer;
import com.athanchang.team.domain.Employee;
import com.athanchang.team.domain.Programmer;
public class TeamService {
/*
* 给memberID赋值时使用
* 即:tid
*/
private static int counter = 1;
private final int MAX_MEMBER = 5;//限制开发团队的人数
private Programmer[] team = new Programmer[MAX_MEMBER];//保存开发团队成员
private int total;//记录开发团队中的实际人数
/*
* 构造器
*/
public TeamService() {
super();
}
/**
* 获取开发团队中的所有成员
* ------
* 作用:
* 功能1、显示团队成员--将团队成员有几个列几个
* @return
*/
public Programmer[] getTeam() {
/*
* 返回的是一个数组,且该数组并不是 这个类的属性数组
* 而是类中的属性数组 中的有效值(不包含null),组成的一个数组
* 用的是:数组的复制
*/
Programmer[] team = new Programmer[total];
for(int i=0;i<team.length;i++) {
team[i] = this.team[i];
}
//返回的是局部变量的这个team
return team;
}
/**
* 将指定的员工添加到开发团队中
* @param e
* @throws TeamException
*/
public void addMember(Employee e) throws TeamException {
/*
* 不满足 所有异常的情况,就成功了
* 否则,不成功
*/
//1、成员已满,无法添加
if(total >= MAX_MEMBER) {
throw new TeamException("成员已满,无法添加");
}
//2、不是程序员,无法添加
/*
* e instanceof Programmer:
* e 是 Programmer 类,或者其子类的对象实例
*/
if(!(e instanceof Programmer)) {
throw new TeamException("该成员不是程序员,无法添加");
}
//3、该员工已在本开发团队中
/*
* 做一个遍历
* 写一个方法来实现
*/
if(isExit(e)) {
throw new TeamException("该成员已在本开发团队中");
}
//4、该成员已是某团队成员
//5、该成员正在休假,无法添加
/*
* 因为employee中没有status属性
* 但是能走到这里的一定时programmer类或者其子类的对象
* 而programmer中是有status属性的
* ------进行强转
*/
//这里不可以是用多态实现,因为是父类向子类转;只有子类向父类转可以用多态
Programmer p= (Programmer)e;//一定不会出现ClassCastException,类型转换异常
//这样更好一些:降低了空指针异常的风险
if("BUSY".equalsIgnoreCase(p.getStatus().getNAME())) {
// if(p.getStatus().getNAME().equals("BUSY")){
//因为NAME是string类型的
throw new TeamException("该成员已是某开发团队的成员");
}else if("VOCATION".equalsIgnoreCase(p.getStatus().getNAME())) {
throw new TeamException("该成员正在休假,无法添加");
}
//6、团队中至多只能有一名构架师
//7、团队中之多只能有两名设计师
//8、团队中最多只能有三名程序员
//获取team中已有的成员中架构师、设计师、程序员的个数
int numOfAch = 0,numOfDes = 0,numOfPro = 0;
for(int i=0;i<total;i++) {
/**
* 注意:这里是一个重点,自己没有想到可以用instanceof来进行判断
* 从小到大的范围比较
*/
if(team[i] instanceof Architect) {
numOfAch++;
}else if(team[i] instanceof Designer) {
numOfDes++;
}else {
numOfPro++;
}
}
//
if(p instanceof Architect) {
if(numOfAch >= 1) {
throw new TeamException("团队中至多只能有一名构架师");
}
}else if(p instanceof Designer) {
if(numOfDes >=2) {
throw new TeamException("团队中至多只能有两名设计师");
}
}else if(p instanceof Programmer) {
if(numOfPro >= 3) {
throw new TeamException("团队中至多只能有三名程序员");
}
}
/*
* 注意:
* 如果这里将每个if的嵌套的条件重叠在一起,是不可以的;
* 例如:
* 在现有的语句下,是说:是--架构师,只要不满足:架构师的数目>=1,就可以添加进去
* 但是,修改时候的是:
* 如果是架构师,且:架构师的数目>=1,
* 不一定 可以将该成员添加进去 --该开发团队中去
* 也可能会 由于满足 第二个或者第三个条件,而不能添加进去,而是直接抛出异常
* 是因为,在这里,三个if语句是直接并列的,
* -----------
* 但是在原来的if-else语句中:
* 一旦进入了是架构师的模块,就不可能再进入下面的模块
* ----->所以,修改前后的情况,实际上是不一样的
*/
//一个团队中,现在只有两个设计师。现在来一个新的员工,恰好是架构师,按照正常的逻辑来讲,是可以添加进去的
//但是,在修改之后(错误的)逻辑中,第一个if语句没有进去,但是判断第二个语句的时候,p instanceof designer也是true,
//所以,进入到这个if语句的语句体中去了--抛出异常“团队中至多有两名设计师”
//不能添加进去这个 架构师
/**
* 以下两个步骤的先后顺序不影响
*/
//将p 添加到现有的team中
team[total++] = p;
//开发团队中额属性赋值
p.setStatus(Status.BUSY);
p.setMemberId(counter++);
}
/**
*
* 判断是定的员工是否已经存在于开发团队中
*/
private boolean isExit(Employee e) {
for(int i=0;i<total;i++) {
if(team[i].getId() == e.getId()) {
return true;
}
}
return false;
/*
* 注意:e 可以走到这一步,那一定是 programmer类或者其子类的对象
* ----一定有memberID的属性
* 因为memberID的属性是在programmer类中定义的
*
* ------所以在这里拿memberID去比较也可以,但是要把e 强转成programmer才可以去比较
* 而且这里的强转一定可以实现
* 因为能走到这里就意味着,一定是一个programmer类或者其子类
* --这里在进入之前,没有进入开发团队过,memberID没有赋过值,用的是默认初始化值---->0
* 此时进去判断,团队中的成员的就不会有谁的 memberID跟他的一样,
* 因为开发团队中的 成员的memberID最少也是从1 开始往后去算
* 如果某个员工之前加入开发团队过,假设第一次添加是3,它的后边还有4,把3这个员工移除
* --移除以后,可以考虑:将3的memberID不去改,但是它的状态是 free,
* 这时候可将3 的memberID 置为0;;或者不置为0 ,认仍旧是3
* 要再次把 3 添加进去,因为后边有一个4,添加成功的话,就是5,因为 开发团队中相当于就没有3的这个数据了
* 这个时候,再次判断 开发团队中有没有3,其实这时候里边就没有3了
* ------>没有3就可以添加成功了,只是把 这个3 重新赋一个值,就是5了,
* ----------->用memberID来判断也行,就是很麻烦
*/
}
/**
* 从团队中删除成员
* ----对应功能3的删除成员功能(memberID)
* 异常:找不到指定的员工
* @param memberId
* @throws TeamException
*/
public void removeMember(int memberId) throws TeamException {
/*
* 遍历现有的team,看是否存在memberID与要查找的相等 的情况
*/
int i=0;
for(;i<total;i++) {
if(team[i].getMemberId() == memberId) {
/*
* 删除:
* 1、将team[]中该index对应的元素拿出来,将后边的数组元素往前挪,并把原来的位于最后的位置上的值,置为null;
* 2、注意的是:从team中删除的元素,只是相当于不在team[]中,但是该对象仍旧在employees[]中,
* 现在将对象从team[]中删除,应该对于employees[]中的该对象做以下操作:
* (1)将它的status改成free(与add时的操作正好相反)
* (2)这块对于memberID,改不改无所谓(但如果是用memberID作为是否已添加在本团队中的判断的话,还是很重要的;如果不这么用,改不改回来就无所谓)
* 注意:在这里,由于调用的话,传递的实参到形参,并没有重新new(实例化)一个对象,而只是将employees[]中的对象元素的地址传递了过来
*/
//找到了这个元素
team[i].setStatus(Status.FREE);
break;
}
}
//未找到指定memberID的情况
if(i == total) {
throw new TeamException("找不到指定的memberId的员工");
}
//找到了这个元素
//后边的元素覆盖前一个元素,实现删除操作
for(int j=i+1;j<total;j++) {
team[j-1] = team[j];
}
team[total-1] = null;//将remove之前的最后一个元素置为null
total--;//team[]减少一个元素
}
}
/*
*总结:
*1、instanceof:
*(自己迷糊了好久)只需记住一句话
* a instanceof A,那么,a所属的类 与 A类,必须是子类和父类的关系
*/
3、Status类
/*
* 枚举类:
* 只有有限个,可以都枚举出来,叫做:有限个的
* 在这个问题中,对象有3个,而且只有3个
*/
public class Status {
//属性
private final String NAME;
//类的构造器要私有化------不允许在外边造对象了
private Status(String name) {
this.NAME = name;
}
/*
* 造3个对象,这3个对象(也可以叫做3个变量)可以直接通过类去调用
* ------声明为public
* 值不可以修改
* -----final
* 常量
* -----static
*/
public static final Status FREE = new Status("FREE");
public static final Status BUSY = new Status("BUSY");
public static final Status VOCATION = new Status("VOCATION");
public String getNAME() {
return NAME;
}
/*
* 需要重写toString
* 因为FREE BUSY VOCATION,是三个对象的NAME属性的值
* 而且对象是静态的(全局常量),类似于 “单例模式”
* 只不过这里是三个对象而已
* ------那么,在重写的时候直接输出对应的 对象的 NAME 属性的值
* 即可
*/
@Override
public String toString() {
return NAME;
}
}
4、TeamView类
import com.athanchang.team.domain.Employee;
import com.athanchang.team.domain.Programmer;
import com.athanchang.team.service.NameListService;
import com.athanchang.team.service.TeamException;
import com.athanchang.team.service.TeamService;
public class TeamView {
/*
* 属性:实际上是NameListService类、类的对象
*/
private NameListService listSvc = new NameListService();
private TeamService teamSvc = new TeamService();
public void enterMainMenu() {
/*
* 将整个界面的逻辑放入一个循环中去 可以实现:菜单栏的重复选择和使用
*/
// while的终止条件
boolean loopFlag = true;
char menu = 0;
while (loopFlag) {
/*
* 保证,只有输入的不是‘1’的情况下,才会在执行操作之后再次输出公司员工列表
*/
if (menu != '1') {
listAllEmployees();
}
System.out.print("1-团队列表 2-添加团队成员 3-删除团队成员 4-退出 请选择(1-4):");
/*
* 输入
*/
menu = TSUtillity.readMenuSelection();
switch (menu) {
case '1':// 团队列表
getTeam();
break;
case '2':// 添加团队成员
addMember();
break;
case '3':// 删除团队成员
deleteMember();
break;
case '4':// 退出功能
System.out.println("确认是否退出(Y/N):");
char isExit = TSUtillity.readConfirmSelection();
/*
* 因为TSUtillity.readConfirmSelection()中, 使用了readKeyBoard(1,false).toUpperCase();
* 将从键盘读入的数据统统转换为大写
*/
if (isExit == 'Y') {
loopFlag = false;
}
/*
* 结束switch
*/
break;
}
}
}
/**
* 显示所有的员工信息
*/
private void listAllEmployees() {
System.out.println("-----------------------------开发团队调度软件---------------------------\n");
/*
* 公司的员工,这个 对象数组 不可能为空,所以可以不进行判空的判断
*/
// System.out.println("");
Employee[] employees = listSvc.getAllEmployees();
if (employees.length == 0) {
// 公司员工数为0
/*
* 注意:这儿里有一个坑: 有时候对于判断一个对象数组是否存有数据,应该将判断条件写为: employees == null ||
* employees.length == 0 这是因为:
* ①如果返回的是一个已经实例化过(new过的)数组,且数组为空的话,可以直接用employees.length == 0来判断
* ②如果返回的是一个没有实例化过的数组(仅仅声明了变量,但是没有new过),判断数组为空的话,应该用employees ==null来进行判断
* 此时,employees是null的话,employees.length,就是空指针了
*/
System.out.println("公司中没有任何员工信息!");
} else {
// 该公司有员工
System.out.println("ID\t姓名\t年龄\t工资\t职位\t状态\t奖金\t股票\t领用设备");
// 遍历输出公司所有的员工信息
for (int i = 0; i < employees.length; i++) {
System.out.println(employees[i]);
}
}
System.out.println("-----------------------------------------------------------------------\n");
}
private void getTeam() {
System.out.println("-------------------团队成员列表------------------");
Programmer[] team = teamSvc.getTeam();
// 因为team[]已经new过了,所以肯定不是一个null
if (team.length == 0) {
System.out.println("开发团队目前没有成员!");
} else {
// 找到team[]
System.out.println("TID/ID\t姓名\t年龄\t工资\t职位\t奖金\t股票\n");
for (int i = 0; i < team.length; i++) {
/*
* 因为前边的重写过的toString()跟这里的要求不匹配, 所以不能在这里只是简单地调用team[i]的toString()
* ------在programer的类中写一个方法,帮助这的输出格式匹配 之所以在programer中写,是因为:
* ①designer、architect都是programmer的子类,可以继承(重写)programmer的方法
* ②根据TeamViewer中的addMember,发现最少是程序员才可以加入开发团队,所以写在employee类中的话,对employee类的对象而言,
* 没有意义
*
*/
System.out.println(team[i].getDetailsForTeam());
// 此时,调用getTeamBaseDetails()这个方法,就看team[i]具体是谁了,是谁就调用谁的对象
// ----即,getTeamBaseDetails()调用的是哪个类中的对应的方法,就看具体是哪个类的实例化对象了
// 因为programmer、designer、architect类中,均有该方法或是 对于该方法的重写
}
}
System.out.println("----------------------------------------------");
}
private void addMember() {
System.out.println("--------------------------添加团队成员-------------------------");
System.out.print("请输入要添加的员工的ID:");
// 从键盘读入要添加的成员的ID
int id = TSUtillity.readInt();
/*
* 通过id找到指定未知的员工,从而才可以将该员工 尝试 加入 开发团队中
*
*/
try {
/*
* 在这里的异常该处理了,因为这里就是最红的调用了
*/
Employee emp = listSvc.getEmployee(id);
// getEmployee(int id)中,可以判断id 对应的员工 是否可以找到
teamSvc.addMember(emp);
// addMember(emp)可能添加不成功------不符合对于:向开发团队添加员工的要求
System.out.println("添加成功!");
} catch (TeamException e) {
// TODO Auto-generated catch block
System.out.println("添加失败,原因:" + e.getMessage());
}
// 按回车键继续
TSUtillity.readReturn();
/**
* 详细的分析可以看TSU illity.readReturn();这块的文档注释
*/
}
private void deleteMember() {
System.out.println("--------------------------删除团队成员-------------------------");
/*
* 开发团队中,有成员存在的情况下,才可以从开发团队中删除成员
*/
Programmer[] team = teamSvc.getTeam();
// 因为team[]已经new过了,所以肯定不是一个null
if (team.length == 0) {
System.out.println("开发团队目前没有成员!");
} else {
System.out.print("请输入要添加的员工的TID:");
/*
* 需注意的是:这里是按照TID,即,memberID进行删除 在employee、programmer、designer、architect几个类中,
* 在employee中,定义了属性id;在programmer中,定义了属性memberId;
*
*/
int memberId = TSUtillity.readInt();
System.out.println("确认是否删除(Y/N)");
char isDelete = TSUtillity.readConfirmSelection();
// 并不是真的删除
if (isDelete == 'N') {
return;
}
// 走到这,说明是:真想删
try {
teamSvc.removeMember(memberId);
// 删除成功
System.out.println("删除成功");
} catch (TeamException e) {
// TODO Auto-generated catch block
System.out.println("删除失败,原因:" + e.getMessage());
}
// 按回车键继续
TSUtillity.readReturn();
/**
* 详细的分析可以看TSU illity.readReturn();这块的文档注释
*/
}
}
public static void main(String[] args) {
/*
* 造一个当前类的对象,然后通过对象调用enterMainMenu 显示主要的界面 ----主要的功能在enterMainMenu中完成
*/
TeamView view = new TeamView();
view.enterMainMenu();
}
}
完整源码
完整的代码放在下面的位置:
源码地址
点开不必VIP,关注即可下载。
行百里者,半九十。加油!