第二次oo博客
一、前言
第4次题目集,第一道题主要考察正则表达式的相关运用,难度比较大,第二题考察java面向对象中聚合关系,因为题目中给出了详细类图,所以代码整体结构比较清晰,难度适中。第三题考察类之间继承,同样题目中给出了较为清晰的类的构建结构以及继承关系,所以难度适中。总体上看题量适中,除第一题外较为复杂,其他两题的难度还是可以接受。
第5次题目集,第一题较为简单考察字符串相应的操作,通过length函数以及简单的循环可以轻松搞定。第二题考察数组相关操作,采用了比较简单的方式——创建一个新的数组进而实现合并,同样比较轻松。第三题,考察冒泡排序,插入排序以及选择排序,在通过参考相应示例了解了算法原理后,发现难度适中。第四题考察正则表达式以及接口的应用,两个都是比较陌生的领域所以相较来说难度非常大。第五题实在习题集四的第二题的基础上进行迭代,考察对聚合关系的理解,因为题目中给出相应类图所以难度适中。总体来看此次题目集题量适中,整体难度适中,个别题难度较大。
第6次题目集,第一、三、四题都是考察正则表达式的运用,但是考察难度相较以往大幅降低,所以还是比较简单。第二题考察字符与ASCII码值之间的关系,比较简单。第五题,考察类的继承及多态,整体还是偏难,通过查阅了相当一部分的资料后才搞清题目思路,在实现过程中也遇到了很多问题。第六题考察接口以及多态性,难度适中,理解相应概念后还是比较容易实现。总体上此次题量适中,难度也适中。
二、设计与分析(重难点题目)
①题目集4(7-2),以及题目集5(7-5)
7-2:
类图:

复杂度:

7-5:
类图:

复杂度:

两次作业需求相似代码逻辑基本相同,整体解题思路是按照题目中所给出的类图逐步写出相关的类。类中方法的解决思路如下(仅给出有一定难度代码,简单的不予展示。给出代码以题目集5(7-5)为例):
1)日期增减:Month类和Day类中的月份或日设为最值的方法是用于对日期进行增减,在对日期进行增时,需要考虑到日期的一般性及特殊性。一般性是增减天数不会出现跨月份甚至是跨年份的情况。特殊性则是要考虑上述情况,需要对日期进行相应判断,例如当日期为12月31号,再增加天数则需将月份和日设为最小值,年份加一,日期减法类似,要考虑到平闰年对2月份天数的影响以及各月天数可能不同的情况。
2)判断日期是否相同:分别比较两个日期的年月日,看数值是否相等。
3)前或后N天:利用循环,进行N次的日期的减或增。注:因为题目集5(7-5)测试点采用了最大整形数,所以在求后N天时进行了一些算法优化。
前:
public DateUtil getPreviousNDays(int n) {
for(int i = 0; i < n; i++) {
day.dayReduction();
if(day.getValue() < 1) {
month.monthReduction();
if(month.getValue() < 1) {
year.yearReduction();
month.resetMax();
}
setDayMax();
}
}
return new DateUtil(year.getValue(), month.getValue(), day.getValue());
}
后:
public DateUtil getNextNDays(int n) {
int year0 = 0; //算法优化
year0 += n / 146097 * 400;
n %= 146097;
year0 += n / 36524 * 100;
n %= 36524;
year.setValue(year.getValue() + year0);
for(int i = 0; i < n; i++) {
if(year.isLeapYear()) {
mon_maxnum[1] = 29;
}else {
mon_maxnum[1] = 28;
}
if(day.getValue() < mon_maxnum[month.getValue() - 1]) {
day.dayIncrement();
}
else {
setDayMin();
month.monthIncrement();
if(month.getValue() > 12) {
month.resetMin();
year.yearIncrement();
}
}
}
return new DateUtil(year.getValue(), month.getValue(), day.getValue());
}
4)检查日期合法性:先判断年份是否在合法范围内,年份合法的话,再判断是否为闰年,是的话将2月最大天数设为29否则为28,再判断日是否大于等于1且小于当月最大天数。
public boolean checkInputValidity() {
if(year.isLeapYear()) {
mon_maxnum[1] = 29;
}else {
mon_maxnum[1] = 28;
}
if(year.validate() && month.validate()) {
if(1 <= day.getValue() && day.getValue() <= mon_maxnum[month.getValue() - 1]) {
return true;
}
}
return false;
}
5)比较日期(a,b,若a>b返回真):先比较年份如果a>b,则返回真,若a<b则返回假;反之判断月份如果a>b,则返回真,若a<b则返回假;反之判断日,如果a>b,则返回真,反之返回假。
6)求两日期间隔:先将二者进行排序(本人习惯前者为较大日期),利用循环,使前者日期每次减少一天直到与前者相等,定义一个整型变量记录循环次数,即为日期间隔。
public int getDaysofDates(DateUtil date) {
int days = 0;
DateUtil date1 = this; //大
DateUtil date2 = date; //小
if(!date1.compareDates(date2)) {
date1 = date;
date2 = this;
}
while(!date1.equalTwoDates(date2)) {
date1.getPreviousNDays(1);
days++;
}
return days;
}
从复杂度来看,以为主体逻辑非常相似所以两次作业代码复杂度相近。主要是对日期合法性的判断以及对日期进行增减时对日期的判断。
例如:
public void monthIncrement() {
int value0 = getValue();
if(value0 == 12) {
year.yearIncrement();
setValue(1);
}
else {
value0 += 1;
setValue(value0);
}
}
从类图分析,两次作业都是以时间相关操作为基础,通过聚合关系将不同类关联起来。但是两次的耦合度还是差距比较大的,同以DataUtil类为例。
首先以判断日期合法性的方法checkInputValidity()为例。
在4.7_2中方法主体为:
public boolean checkInputValidity() {
if(day.validate()) {
return true;
}
else {
return false;
}
}
而在5.7_5中方法主体则为:
public boolean checkInputValidity() {
if(year.isLeapYear()) {
mon_maxnum[1] = 29;
}else {
mon_maxnum[1] = 28;
}
if(year.validate() && month.validate()) {
if(1 <= day.getValue() && day.getValue() <= mon_maxnum[month.getValue() - 1]) {
return true;
}
}
return false;
}
可见二者差距比较明显,显然前者更为简便,后者则需要考虑多种情况。但是很显然前者的耦合度更高,可以参考下面的的代码:
日类中日期判断:

月类中日期判断:

年类中日期判断:

由此可见在4.7_2中判断日期合法性的方法是通过三个类彼此引用从而实现的,虽然整体逻辑相似但5.7_5中采用直接判断年、月、日的合法性,虽然代码略多,但相较前者耦合性更小。其他方法以此类推,主要差别还是在类间方法的调用关系。同时从代码复杂度来看,12、11,二者相似,基本是用于判断输入数据的合法性。
②题目集4(7-3)、题目集6(7-5、7-6)三个题目涉及继承关系及封装、继承、多态、接口等相关操作,难度有所提升。
7-3:
类图:

复杂度:

题目集4(7-3)主要考察继承关系,先创建shape类作为父类,包含一个简单的输出和一个返回面积(初始值为0.0)的方法,创建子类Circle、Rectangle继承于Shape类,通过重写父类方法,实现得到面积的方法。整体思路较为清晰,根据题目提示逐步构建相应类,求几何图形的面积及体积可参照过往所学过的数学公式。继承关系可以参考以下代码:
class Shape{
//求图形面积
Shape(){
System.out.println("Constructing Shape");
}
public double getArea() {
return 0.0;
}
}


其余类以此类推,实现了代码的复用以及清晰的呈现了类之间的关系,通过类图也是能清晰的观察到这点。从复杂度分析,复杂度为17较高,具体来看主要是用于结合了switch的判断输入的合法性,从最初选择的输入判断到每个选择分别判断输入数据的合法性。
7-5:
类图:

复杂度:

、
题目集6(7-5)在题目集4(7-3)(以下简称7-3)的基础上进一步考察了Java中的多态,自我感觉难度是三次题目中最大的一个。从类的数目上看相较7-3少了Ball和Box类,需求上也剔除了求体积的功能。在任务书中给出了大致的类图,在7-3继承关系的基础上将Shape类改为抽象类,运用了接口,通过应用ArrayList 进行类对象的存储,同时题目也要求在List对类对象的排序。整体比较抽象(个人逻辑思维还是欠缺)。通过查阅资料以及参考过往代码最终完成Shape类:
abstract class Shape{
static double sum = 0;
public abstract double getArea();
public abstract boolean validate();
public abstract String toString();
static ArrayList<Double> list0 = new ArrayList<Double>();
public double sumAllArea() {
sum += getArea();
return sum;
}
}
在构建Shape类的时候思考了很久,因为题目中没有明确给出排序、求和两个方法所构建的位置,所以纠结了很久。通过参考之前一道相似类型的题,最终只决定将求和的功能放到Shape中。因为考虑到如果将排序放到Shape中,可能需要没添加一个新的对象就需要排序一次,不如整体进行排序。排序方法(先将List转化为对象数组,后采用冒泡排序法)如下:
Shape []shape = new Shape[list.size()];
list.toArray(shape);
for(int i = 0; i < shape.length - 1; i++) {
for(int j = 0; j < shape.length - i - 1; j ++) {
if(shape[j].getArea() > shape[j+1].getArea()) {
Shape shape0;
shape0 = shape[j];
shape[j] = shape[j+1];
shape[j+1] = shape0;
}
}
}
其他功能通过类图也可以比较容易分析出此处不一 一列举,从代码复杂度来看,22还是比较高的,依旧是归咎于判断输入合法性和循环方面,可能代码在判断合法性逻辑上还存在欠缺吧。此外也进一步提高了代码的复用能力,代码层次及类之间关系也比较清晰。
7-6:
类图:

复杂度:

题目集6(7-6)相较于以上两个题目,此次题目难度较易,主要考察接口及多态性,运用interface接口,创建GetArea为一个接口,无属性,只有一个GetArea(求面积)的抽象方法,通过implements实现Circle、Rectangle接口。整体思路较简单,通过简单的数学公式则可得出结果。具体代码如下(仅以Circle为例,Rectangle类以此类推):
interface GetArea {
public double getArea();
}
class Circle implements GetArea{
private double radius;
Circle(){
}
Circle(double radius){
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
// TODO Auto-generated method stub
double area = Math.PI * getRadius() * getRadius();
return area;
}
}
由类图也可看出类间关系还是比较清晰。从复杂度分析,相较于上两次题目,此次作业功能需求较为简单合法性判断较易 ,复杂度因而较低。
三次题目总的来看,重点是对Java特色的多态及接口考察,需要对相应操作的深度理解,同时也进一步考察对类进行设计的能力。
③三次题目集正则表达式总结:
从题目集4的水文数据校验及处理(以下简称水文处理)到题目集5中统计Java程序中关键词(以下简称关键词)的出现次数 ,以及题目集6中QQ号校验、验证码校验 、学号校验整体难度呈下降趋势。通过题目集6可以看出,我对简单的正则表达式的运用还是有一定思路及解决能力,但是对于较为复杂的数据判断如水文处理就没有解题思路了。关键词的正则表达也是参考了网上的一些思路。总结发现,首先我的欠缺在于不能对数据进行相应分类,习惯于整体操作。其次,对于正则一些特殊处理语法不熟悉,如split(分割)、replace(替换)等,一方面还是由于对数据处理缺乏经验。归根到底还是做题量不足,没有足够的经验,其实通过题目也能感受到正则表达式在生活中的运用极为广泛,所以在这方面还需要更加努力。
④题目集5(7-4)中Java集合框架应用的分析总结:相较于利用数组进行数据的存储,集合类的存储长度和存储对象类型不固定,更适用于java中对象数据的存储,复用性和可操作性更强,同时数据处理的效率也更高。
三、采坑心得
1、在题目集6(7-5)中,在最后提交环节在图形数均为0的测试点报错了,通过简单的排查,发现代码当输入为0时确实存在bug——链表无法获取[0-1]位置的数据,解决加入了一个判断链表长度的语句。心得:在提交代码时最好自己先检查一遍是否有简单的逻辑错误,不自信的可以在debug测一下。
报错信息:

更改后代码片段:
if(list.size() == 0) {
x = 0;
}
else {
x = list.get(list.size() - 1).sumAllArea();
}
测试正常:

2、同样是在题目集6(7-5)中,题目中给出的不合法判断为:如果图形数量非法(小于0)或图形属性值非法(数值小于0以及三角形三边关系),则输出Wrong Format。但事实上在提交代码的过程中圆属性非法报错了,通过与同学讨论发现正确的判断合法性的方法应为:
@Override
public boolean validate() {
// TODO Auto-generated method stub
if(getRadius() > 0) {
return true;
}
return false;
}
而题目中的要求0应该是合法数据,有点不懂。心得:考虑到误解题目的情况,在报错后可以先检查逻辑错误,无误后仍报错的话,可以尝试修改下判定合法性边界。
3、学号校验的正则表达运用中出现错误,在学好序号的测试点出现报错,分析代码逻辑后发现没有考虑到学号为00的不合法数据。
原正则匹配代码:(20){2}((1[1-7])|(61)|(7[1-3])|(8[1-2]))(([0-3][0-9])|(40)),利用测试工具可以发现错误。

更改后正则匹配代码:(20){2}((1[1-7])|(61)|(7[1-3])|(8[1-2]))(([1-3][0-9])|(40)|(0[1-9])),在最后学号后两位的判断中,在原有基础上添加了判断以零开头的两位数。
测试结果:

四、改进建议
1、在题目集4(7-2)中,没有充分理解题意,导致部分题干中给出的方法没有用到,例如将月份或天数设置为最大或最小。具体方法如下(以月份为例):
public void resetMin() {
setValue(1);
}
public void resetMax() {
setValue(12);
}
月份增减的方法:
public void monthIncrement() {
int value0 = getValue();
if(value0 == 12) {
year.yearIncrement();
setValue(1);
}
else {
value0 += 1;
setValue(value0);
}
}
public void monthReduction() {
int value0 = getValue();
if(value0 == 1) {
year.yearReduction();
setValue(12);
}
else {
value0 -= 1;
setValue(value0);
}
}
很容易看出,将月份设置为最大或最小的方法并没有用上,事实上在例如setValue(12)的位置应替换为resetMax() 方法,日期类中也有同样的欠缺。
2、在进行两个对象数据比较时,可以直接调用对象中get方法获取数据。以题目集4(7-2)为例:
在判断两个日期相等的方法中,原代码中定义了许多变量来表示相应数据,事实上可以直接采用get的方法,没必要重新定义变量,这样可以使代码更简洁。
原代码:
public boolean equalTwoDates(DateUtil date) {
int year1 = day.month.year.getValue();
int month1 = day.month.getValue();
int day1 = day.getValue();
int year2 = date.day.month.year.getValue();
int month2 = date.day.month.getValue();
int day2 = date.day.getValue();
if(year1 == year2 && month1 == month2 && day1 == day2) {
return true;
}
else {
return false;
}
}
五、总结
1、通过三次题目集的练习,对老大难——正则表达式有了进一步理解,以及能进行简单的应用,同时有了一定的解题经验,比如可以先对数据进行合理分组,分别进行相应操作,进而简化难度。
2、复习了所学过的一些基本算法比如冒泡排序、选择排序,以及了解了一种新的排序方法——插入排序。
3、通过练习对java中的多态、抽象类、接口、继承及聚合等有了更深刻的理解,学会如何创建抽象类以及将抽象方法实体化。如何通过子类对父类的继承,进而重写父类方法,实现代码的复用功能。如何利用Map、ArrayList等存储对象数据,并进行相应操作。
4、对一些相关语法还有欠缺如String中的split、equals、isEmpty、append等相关方法。
5、对java多态、集合框架的理解还有所欠缺,不能熟练的使用,没有清晰的知识体系。
5、建议:希望老师在后续的学习过程中,可以将练习版块的难度分解,比如题目集6中的正则表达的考察,从各个较为简单子功能出发,最后实现将子功能集合且有一定难度的题目(例如水文数据处理)。我认为这样可以将降低题目难度的跨度,同时能切实的积累一些经验,再解决难题时会更有思路。
浙公网安备 33010602011771号