文章链接lab4实验报告
目录
1.Error and Exception Handling
1.1定义异常类
根据指导书的说明,加上自己的思考分析,我将设计四个异常类。分别处理依赖关系异常,语法格式异常,信息相同异常,还有和资源地点有关的异常。这里定义的四个异常都是checked的异常,所以最后无论在哪里处理是一定要try catch的。
这里只举一个例子,对于异常类的写法,其余的都是类似的。
public class DependencyErrorException extends Exception {//每一个异常类都继承Exception类
private static final long serialVersionUID = 1L;
/**
* 用详细信息指定一个异常
*
* @param message 错误信息
*/
public DependencyErrorException(String message) {
super(message);
}
}
我们在自定义一个异常的时候就可以直接传进去一个字符串,记录信息。
1.2处理输入文本中的三类错误
对于这部分我专门定义了一个异常类GrammarMistakesException。
这个是我的正则表达式。因为我考虑到正确的情况比错误的情况多,正确的情况是占大多数的,**所以我先把所有的文件都读入进来,并且对其进行整体匹配判断,**如果读取文件符合正则表达式,那么便执行创建计划项等等操作。只有当不匹配的时候再判断具体是哪里出错了一行一行的判断错误,并抛出相应异常。
private void checkException(String line) throws GrammarMistakesException {
String[] Begin = line.split("Flight:|Plane:");
String[] Flight = Begin[1].split("\\{DepartureAirport:|ArrivalAirport:|DepatureTime:|ArrivalTime:|\\}");
if (!Flight[0].matches("[0-9]{4}-[0-9]{2}-[0-9]{2},[A-Z]{2}[0-9]{2,4}")) {
if (Flight[0].matches("[A-Z]{2}[0-9]{2,4}")) {
throw new GrammarMistakesException("error:计划项名称中缺少日期");
} else {
String[] xStrings = Flight[0].split(",");
String zimuString = xStrings[1].substring(0, 2);
String shuziString = xStrings[1].substring(2);
if (xStrings[1].matches("[0-9]{4}-[0-9]{2}-[0-9]{2}")) {
throw new GrammarMistakesException("error:计划项名称中航班在日期前面");
} else if (!zimuString.matches("[A-Z]{2}")) {
throw new GrammarMistakesException("error:计划项名称中航班号使用了非大写字母");
} else if (!shuziString.matches("[0-9]{2,4}")) {
if (shuziString.length() > 4) {
throw new GrammarMistakesException("error:计划项名称中航班号数字超过四位");
}
} else if (!xStrings[0].matches("[0-9]{4}-[0-9]{2}-[0-9]{2}")) {
throw new GrammarMistakesException("error:计划项名称中日期格式错误");
}
}
} else if (!Flight[1].matches("[A-Za-z]+")) {
throw new GrammarMistakesException("出发地点错误");
} else if (!Flight[2].matches("[A-Za-z]+")) {
throw new GrammarMistakesException("抵达地点错误");
} else if (!Flight[3].matches("[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}")) {
throw new GrammarMistakesException("起始时间格式错误");
} else if (!Flight[4].matches("[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}")) {
throw new GrammarMistakesException("抵达时间格式错误");
} else if (Begin.length > 2) {
String[] Plane = Begin[2].split("\\{Type:|Seats:|Age:|\\}");
String[] xiaoshuString = Plane[3].split("\\.");
if (xiaoshuString[1].length() >= 2) {
throw new GrammarMistakesException("error:飞机机龄小数位数大于1位");
} else if (!Plane[0].matches("[BN]{1}[0-9]{4}")) {
if (!Plane[0].substring(0, 1).matches("B|N")) {
throw new GrammarMistakesException("error:飞机编号开头不是B或者N");
} else if (Plane[0].substring(1).length() > 4) {
throw new GrammarMistakesException("error:飞机编号数字大于4位");
}
} else if (!Plane[1].matches("[a-zA-Z0-9]+")) {
throw new GrammarMistakesException("error:机型格式错误");
} else if (!Plane[2].matches("([5-9][0-9])|600|([1-5][0-9]{2})")) {
throw new GrammarMistakesException("error:飞机座位数输入格式错误或者不在范围内");
}
}
}
1.3输入文件中存在标签完全一样的元素
对于这个情况只举一个例子就好了,完成的时候根据自己的代码完成即可。对于这种错误每次都抛LabelSameException异常,用法和上面的一样。
因为是否存在多个航班计划项的“日 期,航班号”信息完全一样。这个必须在所有计划项清单里比较,所以判断是否存在相同标签应该在三个collection中判断。
checkPlanningEntryName这个方法便是判断检查计划项名字是否重复,所以每一次在添加一个新的计划项之前便会检查一下是否有标签重复的情况,如果有便会抛出异常,停止添加。
1.4输入文件中各元素之间的依赖关系不正确
因为也是和前面的相似,只是举一个例子就好了。
同一个航班号,虽然日期不同,但其出发或到达机场、 出发或到达时间有差异
对于这个功能也是,必须在collections中完成,所以我也专门定义了一个方法,用来检查这种情况。每一次添加新的计划项之前便会检查。
1.5综述
对于上述四种方式的异常,我的处理方案是所有的ADT都向自己的父类或者调取的类抛出异常,对于使用我们ADT的客户端必须try catch处理这些异常。因为这样客户端能够获取到底出现了什么异常,和异常的具体信息,之后能够作相应的处理,反映给使用程序的人,做相应的修改。
public void actionPerformed(ActionEvent e) {// 设置计划项名称
if (textField_9.getText().matches("[0-9]{4}-[0-9]{2}-[0-9]{2},[A-Z]{2}[0-9]{2,4}")) {
try {
String xString[] = textField_9.getText().split(",");
String nameString = xString[0] + "," + TextFileReader.makeCode(xString[1]);
facadePlanningEntry.checkPlanningEntryName(nameString, "flight");
facadePlanningEntry.addTraceLog("设置计划项名称为\t" + nameString);
name = new String(nameString);
} catch (LabelSameException e1) {
// TODO Auto-generated catch block
textField_18.setText(e1.getMessage());
facadePlanningEntry.addTraceLog("设置计划项名称\t" + textField_9.getText() + "\t冲突");
}
} else {
textField_18.setText("计划项名称格式错误");
facadePlanningEntry.addTraceLog("设置计划项名称格式错误");
}
}
2.Assertion/异常机制来保障pre-/post-condition
航班的起飞时间必须要早于降落时间
因为所有的时间对都必须结束时间在起始时间之后,所以在Timeslot中checkRep()
起飞和降落的机场不相同
对于所有继承NotChangeableOneBeginAndEndLocationEntryImpl这个类的所有子类来说,所有的起始结束地点都不应该相同。所以在NotChangeableOneBeginAndEndLocationEntryImpl中的checkRep中写。
高铁的起至站和各途经车站不能重复
对于所有继承NotChangeableHaveMiddleLocationEntryImpl这个类的所有子类来说,所有的起始结束地点都不应该和中间站相同。所以在NotChangeableHaveMiddleLocationEntryImpl中的checkRep中写。因为中间地点都是用hashmap存储的,所以中间地点添加的时候,不会有相同的情况,相同的地点会自动覆盖掉。所以只需比较起始和结束地点是否和中间地点相同即可。
所分配的各个车厢不能有相同的编号
对于所有继承MultipleDustinguishResourseEntryImpl这个类的所有子类来说,所有的资源都不应该相同。所以MultipleDustinguishResourseEntryImpl中的checkRep中写。
3.Logging
日志,我刚开始想选择阿帕奇公司的log4j,但是网上查了许多资料,说Log4j2是log4j的升级版,安全性等等更具优势,所以使用了log4j2。
对于所有的异常处理,用了Java自带的log4j2。 一共分为两种,一种是trace,就是客户端的所有操作。一种是error,是所有的异常。并且在façade中增加两个方法,一个增加步骤,一个增加error。
3.1配置文件
3.2异常的日志
Façade中写一个专门增加错误日志的方法,客户端在捕获到异常之后,便会调用此方法,添加错误日志。
// 增加错误日志
public static void addErrorLog(Exception e, String message) {
LOGGER.error(e.getStackTrace()[0] + "\t" + e.getMessage() + "\t" + message);//这里利用堆栈跟踪找到发生异常的类的名称,位置等等。
}
结果例子:
对于所有的异常/错误:第一个是发生的时间、异常/错误类型、异常/错误发生的类名和方法名,异常/错误的具体信息、异常/错误处理的结果;
所有的结果都存储在了error.log文件中。(当你写好配置文件的时候,会自动帮你创建文件)
3.3操作的日志
Façade中写一个专门增加操作日志的方法,客户端在执行每一步的时候,便会调用此方法,添加操作步骤日志。
public static void addTraceLog(String message) {
LOGGER.trace(message);
}
对于所有客户端操作,都增加日志记录了操作步骤。这里便不一一举例了。
结果例子
对于所有的操作:第一个是发生的时间、类型、操作。
所有的结果都存储在了traces.log文件中。
3.4日志查询功能
对于log中的SearchLog类,提供了日志查询的方法。
一共分为按时间段、按操作类型、按计划项名字三种类型查询。
3.4.1按照时间查询
为此特意写了一个按照时间排序的Iterator。将其按照时间排序。
主要思想:首先一行一行读取日志操作步骤,构造一个时间和日志对应的map,之后传入Iterator,再遍历,依次添加到list中返回。
点击按照时间查询按钮后,会弹出相应的查询结果。对于App的所有操作都有记录,这里就不一一列举。
结果例子:
3.4.2按照计划项名字查询日志
方法planningEntryNameLogs实现了按照计划项名字查询。返回一个list,里面存储着所有包含计划项名字的操作步骤。
主要思想:依次遍历每一条日志,并进行切分。如果包含要查询的计划项名字,那么便添加到list中。
在箭头处输入要查询的计划项的名字。之后点击按照计划项名字查询日志。之后会弹出查询结果。对于App的所有该计划项名字的操作都有记录,这里就不一一列举。
3.4.3按照操作类型进行查询
方法operationLogs实现了按照操作类型查询。返回一个list,里面存储着所有含某操作步骤的日志。
主要思想:依次遍历每一条日志,并进行切分。如果包含要查询的操作步骤,那么便添加到list中。
在客户端有写好的多种操作可供选择,用户选择后点击按照操作查找日志,便会显示出相应的日志。对于App的所有该操作都有记录,这里就不一一列举。
4.总结
对于报告要求3.4代码覆盖度。3.5spotbug利用工具寻找bug这个对于每一个人不同代码是不一样的这里就不加以阐述了。3.6 debug希望小伙伴们自己动脑,找到Bug吧。如果没有头绪,可以下载实验报告,里面有分析。
健壮性原则:总是假定用户为恶意用户,可能输入任何东西,所以防止用户破坏就是对所有的不合法都要进行处理。对自己的代码要保守,对用户的行为要开放
正确性:永不给用户错误的结果
本次实验重点训练学生面向健壮性和正确性的编程技能,利用错误和异常处
理、断言与防御式编程技术、日志/断点等调试技术、黑盒测试编程技术,使程序可在不同的健壮性/正确性需求下能恰当的处理各种例外与错误情况,在出错后 可优雅的退出或继续执行,发现错误之后可有效的定位错误并做出修改。