本来,这次实验应该写一个实验报告,描述在实验过程当中遇到的一些问题和解决方案。不过最近有很多同学都在纠结这次实验应该从哪下手,我从一个Java
新手的角度帮大家理清一下思路,就当做是实验报告了。(注:由于Java
水平有限,下文中的很多习惯用词可能来自Objective-C
语言。)
我的设计模式灵感来源于著名的MVC
模式,有兴趣的同学可以百度一下。
具体代码可以参见GitHub。
说是学生管理系统,就应该可以预见到,主界面应该是一个有管理功能的表格,按照常理,我们可以添加/修改/删除学生的信息。同时,一个功能完备的系统一定不是随意进入的,所以我们还需要登录模块。登录模块一般包括登陆/注册/忘记密码三部分,这三部分的注册和忘记密码在样式上基本相同,都是用户名占一行,密码和确认密码占两行。
构思好基本的界面之后,我们很有必要把它画下来,方便我们在写代码的时候理清层次。
数据模块的搭建是贯穿整个项目的。第一次的搭建很可能和项目完成的时候相差甚远。我一开始搭建数据模块的时候,自以为方法已经给的尽可能的全,但是标记第一个release的时候,和最初相比多了一倍的代码量。这里,我们强调的是代码在实现某些功能的时候,需要根据需求不断地更新基础需求。
由于我们在管理的是学生的信息,所以一开始需要写学生类。学生类应该包含一些简单的属性,你也可以根据需要在后期添加,同时写好所有属性的get
/set
方法,一定要注意类型。
到这里,看看表,过去了2分钟。在这2分钟里,你完成了这个项目的第一步,同时也是最关键的一步——学生类都搭建好了,管理学生还不简单吗?
但是,如果你管理的只是三四个学生,就像导师带研究生一样,你还可以简单地实例化三四个学生对象就可以了,非常可惜的是,学生管理经常出现大数据——如果你是班主任,你要管五六十个学生,你要是辅导员,就得管几百个学生。一个个实例化显然是不现实的。所以,我们可以选择构建一个线性链表管理学生。
阅读笔记:线性链表是一种具有连接存储结构的线性表。相邻的元素在逻辑和存储地址上可能都并不是相邻的。简单地说,它就是线性结构的“链表”。
读懂上面的内容,就相当于为你在数据结构的学习上做了一点点预习。当然,数据结构并不是这么简单的,线性结构(linear structure)是其中不那么抽象的一部分。考虑到我们管理学生的时候一般并不考虑学生之间的关系,只是在序上有所区别,按序排列,所以学生可以添加到一个线性列表里。
现在,整个项目最复杂的类之一即将开始——你的好友【学生列表】上线了。
学生列表是在项目中的主体功能中最重要的一环。幸好,Java
在表面上并没有C
语言那么复杂的指针,线性链表其实只是一个泛型的数组。如果你的记忆力还好,就会想起实验三中有一个重要的类叫ArrayList
,就是你了。
学生列表的类里,方法会有很多。比如添加/修改/删除某个学生,甚至是某几个学生。获取学生的列表,查找列表当中的某个学生等等。需要注意的是,一定要为每个方法都取一个容易识别的名字,名字不怕长,只怕你以后再也看不懂了。比如添加学生的方法,添加某个学生你可能会写成addStudent(ZHRMoeStudent)
,那么添加很多学生应该取什么名字呢?好的方法是,添加一个学生的时候,方法名就说好添加一个学生,例如addSingleStudent(ZHRMoeStudent)
。(这里的ZHRMoeStudent
只是我假设的学生类的名字)
当你用正常的方法声明学生列表的实例时,依然会像上次一样收到一个提示,泛型的具体类型并没有声明。这次,我们不再使用屏蔽的措施,而是正面地解决它。我们可以用如下的方式声明一个学生列表实例。
public ArrayList<ZHRMoeStudent> studentList = new ArrayList<ZHRMoeStudent>();
如果你只用了3分钟就写好了这个类,我首先要恭喜你有一个飞快的手速,但同时,我也建议你再仔细地想想有没有什么可以添加的方法,免得以后添加的时候打乱编程思路。
登陆的时候,你想判断用户名密码是否正确,应该去找谁呢?答案就是User类。所谓的用户其实就是用这个用户系统的人,他们的属性就是用户名、密码。写好他们对应的get
/set
方法,看看表,2分钟。
注册?忘记密码?如果你想更改用户的信息,要是只实例化了几个用户的对象,肯定是说不过去的。这样的话,我们就应该再给用户建立一个列表。方法和学生的列表类似,都是删除/添加等等。这个过程大概需要5分钟的时间
理论上讲,数据模块的搭建只需要15分钟。虽然你之后肯定还需要再雕琢这一部分的方法,而且这种修改一定是贯穿整个项目周期始终的。但是,迈出了这重要的第一步,意义是非常重大的。还需要额外说明的是,学生列表和用户列表的两个列表属性一定是静态的,不要问我静静是谁。
终于可以画界面了!
为了让你可以更加自由地描绘界面结构,我只提供一些简单的思路。
在画界面之前,一定先在哪里画出这个界面的具体样子,这对你了解这个界面的结构和排列有非常大的帮助。然后,分区域把对应的元素规划成JPanel
类的对象。
由于,登陆界面这个类是一个图形界面,所以它应该继承于JFrame
。
需要注意的是,密码框并不是简单的JTextField
,而是JPasswordField
,这种框当中的文字都会(在表面上)加密成点。
可能用到的界面元素类:
-
区块
JPanel
-
文字
JLabel
-
按钮
JButton
-
输入框
JTextField
-
密码框
JPasswordField
同时,界面上的按钮在点击的时候应该做出反应,这时候,我们应该为每一个按钮都添加事件监听,监听的是这个界面本身。所以相应的语句应该是someButton.addActionListener(this)
。
然后就报错了。
因为,这个界面本身并不是一个动作的监听者,它只是一个界面而已。所以,我们让它实现ActionListener
这个接口。相应的类定义是:
public class ZHRMoeLogin extends JFrame implements ActionListener{
//这个界面的代码
}
报错变成了提示,这个提示告诉我们,接口的方法我们还没有实现。所以接下来,我们要给这些按钮实现动作事件。
public void actionPerformed(ActionEvent a) {
if(a.getSource() == someButton) {
//写关于这个的按钮按下时的代码
} else if(a.getSource() == anotherButton) {
//写关于另一个按钮按下时的代码
}
}
需要解释的是,ActionEvent
是一个动作事件,其实也就是点击了这个界面的某个元素,根据点击不同的按钮,我们可以做出不同的反应。
另一个问题是,用户名和密码需要和之前的某个具体的用户相同,这里我不详细介绍对照的方法,不过搜索一定是一个不错的选择。另外,用户名和密码为空的时候,登陆应该被阻止(一开始,我故意留下了这个漏洞方便其他界面的检查,所以是否在一开始就解决这个问题,依情况而定)。这其中的逻辑,你们可以用离散数学的分析方法做分析(顿时就高大上了)。
注册需要对用户的列表进行添加操作,这里就要看你给用户列表写的方法是否详尽了。
同时,注册新用户的时候,需要输入两遍密码防止用户手残。两次密码需要对照一致才能注册,而用户名不可以是已经注册了的用户名,也要做相应的检查。这其中的逻辑,也应该细致地考虑。
忘记密码界面其实就是修改密码的界面。这个界面的结构和注册界面大体相似,但是不同在于,只有用户名已经存在的时候才能更改密码(即逻辑与注册界面相反)。而用户列表内的操作,可以选择先删除原来的用户,然后把用户名和新的密码重新生成一个用户加入列表当中。
为了让我们写出的主界面美观,代码应该下一番功夫认真排版。
主要的数据都填在了一张JTable
的对象(也就是一张表)里,这张表由于需要滚动,所以要加入JScrollPane
,即滚动的模块当中。
JTable
数据的获取,需要通过setModel
方法设置。
studentListTable.setModel(new TableModel() {
@Override
public int getRowCount() {
return ...;
//这里需要返回列表的行数
}
@Override
public int getColumnCount() {
return ...;
//这里需要返回列表的列数
}
@Override
public String getColumnName(int columnIndex) {
return new String[]{"", "", ...}[columnIndex];
//这里需要返回列标题的序列
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
//这里需要返回类型
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
//这里返回的是列表的内容是否可以修改,这里设置为是,就可以完成学生信息的修改了
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
ZHRMoeStudent s = studentArray.getSingleStudent(rowIndex);
return new Object[]{...}[columnIndex];
//这里返回学生信息,填满列表 需要填写get方法
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
ZHRMoeStudent s = studentArray.getSingleStudent(rowIndex);
switch (columnIndex) {
case 0:
... //填写第0列的set方法,以下依此类推
break;
case 1:
...
break;
case 2:
...
break;
case 3:
...
break;
case 4:
...
break;
case 5:
...
default:
break;
}
//修改列表元素
}
@Override
public void addTableModelListener(TableModelListener l) {
//重写方法
}
@Override
public void removeTableModelListener(TableModelListener l) {
//重写方法
}
});
通过这些代码,列表就能够得到数据了。
添加学生的界面应该是可以获取学生的所有属性的。注意,有些属性例如“性别”,让用户去输入显然是可能出现“手残”的情况的。为了避免这种笑话的发生,我们可以添加两个JRadioButton
分别表示男女,然后把两个选项放进一个ButtonGroup
里,使得这两个选项只能单选。
这两个JRadioButton
也应该是动作的监听者,用来设置性别的男女。
图形界面是不是不好画啊,是不是要写好多东西啊?
可是,用代码写界面的确是这么复杂的,但通过一些字符就可以绘画出一个有意义的界面出来,的确非常有成就感。
总的来说,到这里我们的项目中的基本功能已经实现了。但是很有可能出现某些不可预料的奇怪情况,bug。遇到bug不要着急,这是提升我们能力的好机会。好在这个项目的数据规模并不复杂,bug的原因经常显而易见。如果真的遇到了不可解决的bug,那么在实现了所有基本功能的前提下,舍弃一些极难实现的功能,也是情有可原的。否则,高端的程序员存在的意义是什么呢?
在项目过程当中遇到问题,可以找我,也可以找其他在Java
方面更有研究的同学解决,希望大家可以深刻地理解设计的步骤,对未来设计更大的项目有所帮助。
2015.4.17 C2613次列车上 一稿
2015.4.19 C2076次列车上 二稿
ZHRMoe Studio, 2015.