已经不是第一次敲Java了,真的应该感谢学院在大一暑期的时候开设了面向对象先导课程,让我好歹磕磕绊绊地过了编手,不至于在OO课来临直接过于难受。其实我跟JAVA有着很大的缘分,记得大一的时候,学院给我们开设了第二课堂,大部分同学为了给下学期的数据结构打好基础选择了C语言,偏偏我头铁,以为要把眼光放得长远选择了Java。结果可想而知,一点点编程基础没有的我在满是大佬的课堂疯狂划水,讲的东西一个字都听不懂,整个学期唯一的收获大概就是电脑上安装了Eclipse。暑期课程抢救了我,在安装了Eclipse的基础上敲了点代码,入了门,顺承地来到了OO的课堂。
一、以“码”为鉴,可知己菜
太菜了,是真的菜,这大概是我最近这次互测拿到对方代码之后不由得发出的感慨,别人是面向对象编程,而我是真的面向对象编程,或者说:面向对象写面向过程编程。
1、第一次作业——重拾Java
虽然之前有过编写Java程序的经验,但那个时候也只是简单地学习了语法入了门,没有理解Java编程的精髓,写出来的程序有着浓厚的C语言味道,再加上半年来没有加强训练,第一次作业依然是在做回顾语法的过程。整个工程文件只有Main和Poly两个类,所有的方法都在Poly里面,土豪式地实例化了20个Poly对象,每个都开了1000000大小的数组(指数从0到999999,以下标为指数,以内容为系数),这种做法极大地简化了逻辑,不用排序,也不用担心数组会爆,反正大于999999的指数就是ERROR,很暴力地做输入、正则表达式分解、多项式相加、输出。然后再自以为安全地写了通篇的try-catch,搞定。
结果测试结果一出,就傻眼了。因为粗心,没有看到作业指导书里面最多只允许6个字符长度的数字,误以为最多支持6个前导0就行,导致公测挂了一个点。互测的时候又被挂了一个crash,没错,通篇try-catch了也会crash?也希望各位同学引以为鉴,不要以为try-catch就安全了。因为我在正则表达式部分写了通配符\\*,导致对于无限长的合法输入(100个多项式)也纳入到了考虑范围中,导致运行时爆了Java的运存,连catch都崩了更不会catch到exception了。第一次作业我从中吸取了很多教训,这之后我除了catch(Exception e),还catch(Error e),彻底杜绝了crash。
很凑巧又不凑巧的是,第一次作业我分配到了室友的代码,室友之间肯定就这一次的作业有过交流,我们两个的思路也是一样的,虽然我还是尽可能地分析了代码,但找不到任何bug。
2、 第二次作业——初试面向对象
从第二次作业开始,无论是老师还是同学都在刻意地学习面向对象式的写法。从这一次作业指导书可以看出来,强行要求实现五个类就是要学生去体会“对象”这个概念,制止过程式写法。这一次的作业难点在于电梯时间和请求时间不同步,有一部分同学选择模拟时间的方式将两个时间调同步。这一种方法可以简化逻辑,并且可以适用于接下来的任意一个电梯作业,但模拟时间循环会导致程序运行完毕可能需要较长时间。我采用了另一种方法让二者时间同步:在电梯每开始执行一条指令到执行完毕的这一段时间,记录所有请求的到来情况,若某个按钮没有被点亮,则接受请求,即点亮按钮,否则标记为同质请求。老师说过,面向对象实际上就是在搭积木,我给每个楼层都设置了按钮,电梯里也设置了按钮,整部电梯系统就是由楼层,电梯,调度器搭起来的,再搭配上请求类和请求队列类。写完之后颇有一丝面向对象的意味,就好像自己在minecraft里搭房子一样。但是调度器类我觉得自己依然使用的面向过程的思路,不同于“楼层”、“电梯”、“请求”,我不能很好的把“调度”这一概念很好地抽象出来,也没有太关注这一点。抽到的代码也是洋洋洒洒一百行的调度类,跟我有着一样的毛病。
在测试自己的代码的时候,我和小伙伴们一起对老师给出的错误分支树每一个分支都写了样例,尽可能降低bug出现概率。这次业我吸取了上一次的教训,认真阅读了作业指导书,并对照着自己的代码一遍又一遍检查,所幸使得自己公测和互层都没有被扣分。但是我分配到的代码也是一个大佬的,用网站测试了我们构造的所有测试样例之后也没有发现他任何bug,证明了他代码逻辑的正确性。进而进行鲁棒性测试,测试极端输入和各种错误输入,也没有发现问题,正则表达式的匹配部分也运行良好,不会因为奇特的情况crash,让我心灰意冷。灰心之际突然发现他的编码格式是GBK,勉强报告了一个incomplete。
3、第三次作业——继承&接口
这一次的作业可以说是很有难度了,新的要求使时间不同步的基础上增加了新的困难:捎带会影响同质性。因为要用继承,所以并不想破坏第二次的大体思路,这意味着不能在扫描队列时先判断同质再判断捎带,而要每条指令都去判断是否同质,是否可以捎带。捎带不但可以捎带新来的请求,也可以捎带之前的请求,这就使扫描不能从电梯开始运动时刻开始,要从未执行的第一条指令开始。除此以外小问题众多:主指令变更、同层输出顺序等等。在敲代码之前我进行了很长时间的构思,这也是我在学习数据结构开始总结到的经验:先想好再动手,不然debug的时候可能会哭。整体架构设计完后也想了很多特殊情况:主指令和捎带指令同层结束但捎带指令先执行。这种事无巨细的构思给我后期的码代码带来了无限的便利。
但是过分关注逻辑性,又让我忽视了“对象”这个概念一个调度器类写了300行,别的各个类最多也就50行,让整个结构显得很不均匀,调度器承担了太多的任务。因为测试期间没有看到优秀的“面向对象”代码,自己也不知道怎样的写法才是最好的,直到这一次的互测分配。虽然这个代码被我找到了同层输出顺序的bug,但他代码的抽象能力是真的很厉害,第二次作业就很好地完成了调度器的抽象,甚至把输入、输出都抽象了出来,既提高了代码的可读性,让代码更整洁,也减小了第三次作业的任务量,不足数十行代码,重构一小部分,很漂亮地完成了任务。学习了他的写法之后我决定小范围重构自己的代码。
不过值得开心的一件事情就是本次作业依旧完美通过了公测和互测,这一点十分得益于对错误分支树的遍历。我认为是可以总结为经验的形式继续下去的。
二、感想
经过了几次OO作业,我觉得OO课程最恐怖的地方是他的作业周期。最好的方式是从周日开始构思,周一敲代码,周二完成,周三稍作修改收工。但凡犯了一点点拖延症,敲代码的时候就会很被动,ddl压头难免会恐慌,就会造成琢磨不透彻,思路不清晰,bug繁多的现象给别人送去一份大礼包。最近愈发感觉Java比C友好,记得第一次作业的时候还要求写了一个C代码,在享受过Java的Arraylist的便捷和Eclipse的自动补全之后,开始写C语言竟然还有那么一丝茫然。以前听大神说C是高级语言里的最低级语言,看来是没错了。
前文说过写面向对象的代码就像在minecraft里面搭房子,这确实是我真正尝试用面向对象的思想写Java的感觉。这不过现在房子搭地还不够好,还要经过学习去研究怎么搭地基,怎么搭房顶。特别是接口的用处还没有学懂,上节课老师也说过接口在这次作业看似用处不大,不过在多线程作业时就会大放光彩,但愿自己可以伴随着学习一点一点吃透Java的编程思想。最近在基友群里看到了一个图片,很生动地解释了面向对象思想中各个专有名词的概念。
这幅图片蛮搞笑的,不过确实能通俗地解释基本概念,算得上是忙碌的学习生活中一点点乐子吧。也希望自己的OO课程可以在忙碌得过程中伴随着乐子,充实愉快得度过这个学期。