OO面向对象课程作业1-3总结
作业一、多项式的加减运算
1、设计要点与自我分析
我设计的类图
老师建议类图
我设计了两个类来进行多项式的计算,类Polynomial进行多项式的存储和输入输出,第二个类进行多项式加减运算。而加减运算的类里面只有方法,而且都是静态方法,没有存储变量,感觉这个设计还是有些问题。之后我也参考了一下别人的代码。
- 多项式的存储:
我用的是一维数组来存储多项式,下标代表的是多项式的次数,数组存储的是多项式的系数,这种存储方法对于多项式加减法的操作很方便,但是浪费的空间大,因为很多系数是0;而且存储多项式加减式子的时候也开了大数组将所有数据装进去,一次性进行计算,这样也浪费了大量的空间。
参考了大佬的代码,学习了一下如何管理多项式的存储和加减:
1、专门设出一个类来存多项式的每一项,包括系数和次数,然后开一个ArrayList<Object>来存储这个类。
2、加减法操作,将所有项加入到原来的ArrayList里面,然后在ArrayList里面对指数相同的项进行合并操作,再进行对指数从小到大的排序,用了sort()函数。
2、所用知识
1. 去除空白字符
s = s.replaceAll("\\s*", "");
2. 正则表达式的使用
1、使用group()捕获组:
捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。group(0)代表的是整个匹配的表达式,之后每遇到一个左括号,group的索引值加一。
- public String group( )
返回上次匹配操作(比方说find( ))的group 0(整个匹配)
- public String group(int i)
返回上次匹配操作的某个group。如果匹配成功,但是没能找到group,则返回null。
2、非贪婪匹配:
当“ ?”字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m}之后时,匹配模式是”非贪心的”。”非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的”贪心的”模式匹配搜索到的、尽可能长的字符串。
*、+限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个?就可以实现非贪婪或最小匹配。
3、判断两个字符串内容是否相等
需要用str.equals()的方法判断,而不能使用“==”号。
4、错误的捕捉,使用try-catch和throw
try{
... ...
}catch(IOException e){
//处理IO异常的代码
}catch(NumberFormatException e){
//处理parseInt不能转换时的异常代码
}catch(StringIndexOutOfBoundsException e){
//处理数组越界的异常代码
}catch(Exception e) {
//总异常(父类)
}
也可以自定义异常,如果不满足某项条件则抛出异常
if(!m.find()){
throw new PolynomialError();
}
5、结束进程System.exit(0);
6、可以重写toString()的方法返回多项式的值。
3、遇见BUG以及改正方法
1、正则表达式太长,在数据压力大的时候可能会爆栈
我将整个多项式加减法的式子用正则表达式一次性匹配检查格式是否正确,但由于表达式太长,数据压力大而爆栈。应该逐个多项式进行匹配,逐个向后查找。
2、系数相加的时候容易超出范围
使用java异常处理语句进行异常的捕捉
3、输出多项式的时候没有判断是否是输出第一个大括号,导致前面出现逗号。
加入第一次进入输出循环的判断。
4、在本题目的多项式匹配当中,可使用 **(^|\\+|-)(\\{.*?\\})**来匹配其中加减的操作项。
但是如果两个加减项之间出现了非法字符,正则表达式会自动跳过非法字符去匹配下一项,要怎么解决这个问题呢?我想到三种方法:
(1)将匹配成功的字符串删除以后,用^从头开始匹配。代码如下:
String pattern = "^(^|\\+|-)(\\{.*?\\})";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(line);
while (m.find()) {
System.out.println("Found value: " + m.group());
line = line.replace(m.group(),"");
m = r.matcher(line);
}
(2)每次记录匹配字符串的长度,然后依次累加,如果发现累加得到的字符串长度和读到的m.start(0)索引值不一致,则中间有非法字符。
(3)用region(start,end)重设m的范围。
4、Eclipse的使用总结
1. 导入工程
-
设置工作空间(Workspace)有明显的层次结构。 项目在最顶级,项目里头可以有文件和文件夹。插件可以通过资源插件提供的API来管理工作空间的资源。
-
首先打开eclipse软件,找到左上角File然后点击,然后我们选择Import,点击Import
-
点击Import后,会弹出Import窗口,然后找到General,点击General左边小三角,然后选择Existing Projects into Workspace
-
有时候点导入文件会出现提示
Project is not a Java project.
有几种情况:
-
package名字不相符:右键项目->Build Path来更改source folder的设置
-
eclipse 工程没有build path,则在项目.project文件中添加
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
- JDK版本不对: 项目右键 –>properties–>Java Build Path–>Libraries 然后将JDK换成你当前的JDK版本
2、修改字符集
默认情况下 Eclipse 字符集为 GBK,但现在很多项目采用的是 UTF-8,这是我们就需要设置我们的 Eclipse 开发环境字符集为 UTF-8, 设置步骤如下:
在菜单栏选择Window -> Preferences -> General -> Workspace -> Text file encoding ,在 Text file encoding 中点击 Other,选择 UTF-8。
3、调试快捷键
F11――进入DEBUG视图
F5——进入:移动到下一个步骤,如果当前行有一个方法调用,该控件将会跳转到被调用方法的第一行执行。
F6——跳出:移动到下一行。如果在当前行有方法调用,那么会直接移动到下一行执行。不会进入被调用方法体里面。
F7——返回:从当前方法中跳出,继续往下执行。
F8——移动到下一个断点处执行。
作业二、电梯的简单调度
1、设计要点与自我分析
逻辑结构图:
类图:
老师建议类图:
本次作业的设计要点是多个类进行协同,同时使用队列进行调度。我将请求加入到请求队列当中,由调度器从请求队列中读取请求对电梯类进行调度和操作,楼层类我没有实际用上,这也是设计上的不均衡。其他几个类职责比较分明,调度逻辑清晰。
2、所用知识
LinkedList的使用,由于本电梯使用的是队列的结构,使用了如下几种方法:
(1)q.offer(e) 在链表尾部插入元素
(2)q.peek() 获取第一个元素
(3)q.remove(e) 删除一个元素
(4)q.poll(e) 查询并移除第一个元素
(5)遍历链表:
//for循环遍历
for(int i = 0; i < linkedList.size(); i++){
linkedList.get(i);
}
//Foreach遍历
for(Integer i : linkedList);
//迭代器遍历
Iterator<Integer> iterator = linkedList.iterator();
while(iterator.hasNext()){
iterator.next();
}
但是迭代器遍历和foreach遍历的时候因为是动态删除链表会发生线程错误,所以我用了for循环遍历。
3、遇见BUG以及改正方法
我使用了LinkedList()来存储请求,每次执行一次请求会遍历链表看看有没有同质请求,有的话进行删除。但是删除以后链表的总长度会改变,所以删除以后要将索引值减一。在debug的过程中找出了这个错误,后来在公测的时候顺利通过了。
我测试的同学没有用正则表达式,全部用if-else判断,程序容错能力过差,导致公测全部没有过。
作业三、具有捎带功能的电梯调度
1、设计要点与自我分析
- 提高资源利用率是调度算法设计的核心目标
- “顺路捎带”:在去响应一个请求的路途中可以把资源共享给顺路可完成的请求
类图:
老师推荐设计:
我顺路捎带的思路是找到主请求,然后遍历队列后面可以被捎带的请求,找到离主请求最近的请求执行,若超出主请求的执行时间,便执行主请求,然后选择捎带的第一条消息作为主请求。但是这个设计思路遇到了很多问题,后来参考了别人的思路。可以将每次所能找到捎带方向上的最高楼层作为目标楼层,然后停靠中间的楼层,这样的思路简洁清晰,也省去了很多判断的步骤。
2、所用知识
(1)类的继承
使用继承机制,增加一个子类来重写第二次作业中的schedule方法,需要注意子类不能继承父类的构造器,但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。 如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。
(2)使用interface来归纳电梯的运动方法。
(3)重写toString()方法来获得电梯运行状态和时刻的观察。
3、遇见BUG以及改正方法
在这次的作业中,由于我每次都是找到可捎带请求的最小请求,但是在主请求更换的时候,我将主请求设为了可捎带的最小请求,而可捎带的最小请求在前一条捎带请求的后面发出,这就造成了后发出的指令先执行了。参考了其他同学的捎带思路,即找到当前方向上可捎带的最高楼层进行停靠,可以简化此问题。
其次就是输出错误格式的问题,INVALID的输出的是原请求,而我将请求的括号换掉了,导致很多bug。
测试策略
一开始我是根据错误分支树来构造测试用例,后来的作业中,我根据别人的代码结构来构造测试用例,发现代码中的逻辑错误和bug。
心得体会
作为刚接触面向对象的小白,第一次作业布置下来,就开始两天速成Java,正则表达式,很多资料看得一脸懵逼,期间总是去打扰大佬,问一些十分基础的问题。但在后面两次作业中,对整个编写流程开始熟练起来,也开始理解面向对象的编程思想。每次作业做完以后,也会参考一下别人的代码,和自己的做一下对比,看看那些地方能更加简洁的表达,更高效的处理,同时也感谢能将代码分享给我的同学~
还有就是一定要仔细阅读指导书,我就是因为指导书没有好好看,导致程序出了很多bug,第一次作业由于输出提示没有#提示,挂满了错误分支树,可以说还是很绝望的。第三次的指导书和第二次的差不多,我又很粗浅地看了,差点漏掉了重要信息,第一条指令有限制。后来是在看讨论区的讨论才发现了这个问题。
前期的逻辑结构设计一定要清晰,否则无脑开始写代码真的会遇到特别多问题。
转系之后感觉自己真的特别菜,改bug经常改到绝望,但还是真心感谢一些能够和我交流的小伙伴。