OO第二次作业总结
OO第二次博客作业
一、前言:关于java&面向对象
有了前几次作业的经验,java这门面向对象的语言的冰山一角被我所发掘。了解了java解决实际问题的思路。
类的概念(我的理解):
把需要解决的问题细化拆分,例如一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。简单通俗的来说:一个类只负责一项职责。
复用性也是一个必须考虑的点,一个好的程序员一定是用最少的代码实现最多的功能,代码复用是程序员所追求和期望的,这也是Java语言所提倡的。例如我要创建很多的同学对象,每个同学都有不同的名字,不同的学号,这是OK的。但是每个同学都一样的就是上课,吃饭,喝水。这些就不必要在每一个类中都写对应的方法,可以弄一个学生类包含这些方法。其他类继承该方法,然后根据实际情况进行重写和重载。
话不多说,让我们看下分别看下三次题目集考察的具体内容。
第四作业主要考察的是正则表达式与类的继承。第四次作业7-1考察的是水文数据的判断。一开始我并没有看懂这水文数据的校验和处理是啥意思,后面具体了解后发现其实不难,就是水位,流量和开度这类的,根据正则表达式进行判断然后split就行了。7-3则是考察了类的继承,子类和父类的关系。考虑到代码的复用性,不得不说继承是一个好东西,将不同类的公共属性和方法提取出来成为一个父类。这些公共属性和方法虽然不完全一样,像圆的面积和正方形的面积计算公式不同,但他们都是计算面积的方法,只需要一个shape类,然后子类重写父类的计算面积的方法就好啦。
第五次作业的难度下降了许多,但是题目量确实增加了,这一改进让同学们可以更好的打基础。主要就是一个关键字统计,各种日期的计算。还是逃不开正则表达式,不过经过前几次的锻炼,正则表达式确实吃的比较透,果然训练是非常重要的。
第六次作业考了简单的几个正则表达式和继承与多态,可以说是难度最低的一次作业,虽然代码量不少,但都是比较基础的应用,让我又重新复习了一遍之前的知识。
二、设计与分析
1、日期类聚合设计的优劣比较:
(1)题目集四(7-2)

import java.lang.invoke.SwitchPoint; import java.util.Scanner; public class Main { public static void main(String[] args){ Scanner in = new Scanner(System.in); int year,month,day,year1,month1,day1; int x; int i; i = in.nextInt(); DateUtil dateUtil; DateUtil date; switch(i){ case 1: year = in.nextInt(); month = in.nextInt(); day = in.nextInt(); x = in.nextInt(); dateUtil = new DateUtil(year,month,day); if(month<=0||!dateUtil.getDay().validate()){ System.out.println("Wrong Format"); }else{ date = dateUtil.getNextNDays(x); String str = date.showDates(); System.out.println(str); } break; case 2: year = in.nextInt(); month = in.nextInt(); day = in.nextInt(); x = in.nextInt(); dateUtil = new DateUtil(year,month,day); if(month<=0||!dateUtil.getDay().validate()){ System.out.println("Wrong Format"); }else{ date = dateUtil.getPreviousNDays(x); String str = date.showDates(); System.out.println(str); } break; case 3: year = in.nextInt(); month = in.nextInt(); day = in.nextInt(); year1 = in.nextInt(); month1 = in.nextInt(); day1 = in.nextInt(); dateUtil = new DateUtil(year,month,day); date = new DateUtil(year1,month1,day1); if(month1<=0||month<=0||!date.getDay().validate()||!dateUtil.getDay().validate()){ System.out.println("Wrong Format"); }else { if(dateUtil.equalTwoDates(date)){ System.out.println("0"); }else { int n = dateUtil.getDaysofDates(date); System.out.println(n); } } break; default:System.out.println("Wrong Format"); } } }
相比与题目集五7-4的日期类的聚合设计本题略有不足,一是代码的复用性更差,每个日期类中,即天、年、月类中都要有一个判断输入是否正确的方法,代码的复用性不高。而且假如你需要调用year类里的方法,你需要很多的get方法直到获取year这个对象才能获取year里的方法。
(2)题目集五(7-4)

对于本题DateUtil和Year、May和Month类聚合,可以更方便的使用各个类中的方法,而且只需要一个方法来判断传入数据的正误。
2、封装继承多态与接口:
(1)题目集四(7-3)
题目要求:
编写程序,实现图形类的继承,并定义相应类对象并进行测试。
- 类Shape,无属性,有一个返回0.0的求图形面积的公有方法public double getArea();//求图形面积
- 类Circle,继承自Shape,有一个私有实型的属性radius(半径),重写父类继承来的求面积方法,求圆的面积
- 类Rectangle,继承自Shape,有两个私有实型属性width和length,重写父类继承来的求面积方法,求矩形的面积
- 类Ball,继承自Circle,其属性从父类继承,重写父类求面积方法,求球表面积,此外,定义一求球体积的方法public double getVolume();//求球体积
- 类Box,继承自Rectangle,除从父类继承的属性外,再定义一个属性height,重写父类继承来的求面积方法,求立方体表面积,此外,定义一求立方体体积的方法public double getVolume();//求立方体体积
- 注意:每个类均有构造方法,且构造方法内必须输出如下内容:Constructing 类名
- 每个类属性均为私有,且必须有getter和setter方法(可用Eclipse自动生成)
- 输出的数值均保留两位小数
主方法内,主要实现四个功能(1-4): 从键盘输入1,则定义圆类,从键盘输入圆的半径后,主要输出圆的面积; 从键盘输入2,则定义矩形类,从键盘输入矩形的宽和长后,主要输出矩形的面积; 从键盘输入3,则定义球类,从键盘输入球的半径后,主要输出球的表面积和体积; 从键盘输入4,则定义立方体类,从键盘输入立方体的宽、长和高度后,主要输出立方体的表面积和体积;
假如数据输入非法(包括圆、矩形、球及立方体对象的属性不大于0和输入选择值非1-4),系统输出Wrong Format.
这题的要求很多细细分析瞎来总共就几点,new 对象是的数据要符合要求,new了对象是输出对应对象的名称,求各种对象的对象面积或体积。
用到了重写父类继承来的求面积的方法数据的封装。
import java.text.DecimalFormat; import java.util.Scanner; class Shape { public double getArea(){ return 0; } } class Circle extends Shape{ private double r; public Circle(Double r){ if(r>=0){ System.out.println("Constructing Shape"); System.out.println("Constructing Circle"); this.r = r; } else{ System.out.println("Wrong Format"); } } public double getArea(){ double a = Math.PI; return r*r*a; } public double getR() { return r; } public void setR(double r) { this.r = r; } } class Box extends Shape{ private double a,b,c; public Box(double a,double b,double c){ if(a>=0&&b>=0&&c>=0){ System.out.println("Constructing Shape"); System.out.println("Constructing Rectangle"); System.out.println("Constructing Box"); this.a = a; this.b = b; this.c = c; }else{ System.out.println("Wrong Format"); } } public double getArea(){ return a*b*2+b*c*2+2*a*c; } public double getVolume(){ return a*b*c; } } class Ball extends Shape{ private double r; public Ball(double r){ if(r>=0){ System.out.println("Constructing Shape"); System.out.println("Constructing Circle"); System.out.println("Constructing Ball"); this.r = r; }else{ System.out.println("Wrong Format"); } } public double getArea(){ double a = Math.PI; return a*r*r*4; } public double getVolume(){ double a = Math.PI; return a*r*r*r*4/3; } }
(2)题目集六(7-5)
题目要求:

- 从键盘首先输入三个整型值(例如a b c),分别代表想要创建的Circle、Rectangle及Triangle对象的数量,然后根据图形数量继续输入各对象的属性值(均为实型数),数与数之间可以用一个或多个空格或回车分隔。如果图形数量非法(小于0)或图形属性值非法(数值小于0以及三角形三边关系),则输出Wrong Format。
- 如果输入合法,则正常输出。
对于此题共有三个图形,圆、三角形和长方形。分别要求各个图形的面积,每个图形都有自己的面积计算公式,每个图形都需要判断输入是否合法。我的解决方案时写一个父类Shape里面写入获取面积,判断输入是否合法的方法,然后继承与三个子类,子类根据图形的特点重写方法。各类数据进行封装,保证数据的安全,不会出现半径或者边长为零或负数的情况。
有个点需要注意,因为是通过输入来确定图形的个数,我采用的是将所有图形加起来申请数组,然后根据每个图形的个数来进行操作。当三个图形加起来小于零是就造成了申请一个大小为负数的数组,造成不必要的错误,所以对于数组的操作一定要慎重小心。
import java.text.DecimalFormat; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Main{ public static void main(String[] args){ DecimalFormat df = new DecimalFormat("#0.00"); Scanner in = new Scanner(System.in); int n=in.nextInt(),n1=in.nextInt(),n2=in.nextInt(); int x=n+n1+n2; boolean flag=true; if(n<0|n1<0|n2<0|x<0){ flag = false; x=0; } double a=0,b=0,c=0; double []s = new double[x]; for(int i=0;i<x;i++) { if (i < n) { a = in.nextDouble(); if (a <= 0) { flag = false; } else { Circle circle = new Circle(a); s[i] = circle.getArea(); } } else if (i < n1+n) { a = in.nextDouble(); b = in.nextDouble(); if (a <= 0 | b <= 0) { flag = false; } else { Rectangle rectangle = new Rectangle(a,b); s[i] = rectangle.getArea(); } } else{ a = in.nextDouble(); b = in.nextDouble(); c = in.nextDouble(); if(a<=0|b<=0|c<=0|a+b<=c|c+b<=a|a+c<=b){ flag = false; }else{ Triangle triangle = new Triangle(a,b,c); s[i] = triangle.getArea(); } } } if(flag){ double sum=0,temp=0; System.out.println("Original area:"); for(int i=0;i<x;i++){ sum+=s[i]; System.out.print(df.format(s[i])+" "); } System.out.println(); System.out.println("Sum of area:"+df.format(sum)); for(int i=0;i<x-1;i++){ for(int j=0;j<x-1;j++){ if(s[j]>s[j+1]){ temp = s[j]; s[j] = s[j+1]; s[j+1] = temp; } } } System.out.println("Sorted area:"); for(int i=0;i<x;i++){ System.out.print(df.format(s[i])+" "); } System.out.println(); System.out.println("Sum of area:"+df.format(sum)); }else{ System.out.println("Wrong Format"); } } } class Shape { public double getArea(){ return 0; } public void validate(){ } @Override public String toString() { return super.toString(); } } class Circle extends Shape{ private double r; Circle(){ } Circle(double r){ this.r = r; } public void setR(double r) { this.r = r; } public double getR() { return r; } public double getArea(){ double s; double x = Math.PI; s = r*r*x; return s; } }
3、我对正则表达式的分析与看法:
这几次作业都与Regex有交集,经过这么多次的被折磨,我也算有了一点心得。我认为正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
1、懒惰匹配和贪婪匹配,意如其名,懒惰就是尽可能少的匹配字符,有时我们可能更加需要懒惰匹配。贪婪匹配会匹配尽可能多的字符。
例如当你的正则表达式写的是1.*2他是贪婪的假如有一个字符串133322他会匹配全串。而当你写的是1.*?时他会匹配13332,当他找到第一个2时他就会停下来,这是懒惰匹配,贪婪匹配就是找到最后一个2停下来,有时形如1.*2这样的正则表达式是很不好用的.*可以匹配任何字符,也就是说你可能会匹配到你不想要的字符,会出现多匹配的情况,这时你就会知道使用懒惰匹配是很重要的。
2、
/w等于[A-Za-z0-9],我一开始不知道可以使用类似[A-Z0-9]这种东西,我还以为[]括号里只能出现一个如1-9的,转念一想在字符串中1和z和.这些并没有什么区别,无非ASCII不同。{6,16}表示范围或是区域,其他的无非是一些细枝末节,这里就不一一写出。
4、java集合框架应用的分析总结:

(引用冰湖一角的图片)
集合是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。其中,构成集合的这些对象则称为该集合的元素。把集合看成是一个不定大小的桶,桶里可以装具有某种映射的数据,例如将学号与姓名。它有点像是C中的链表,malloc动态申请空间,结构体中可以保存具有某种映射的元素。
它比数组最大的优势就在于数组长度不可变化而且无法保存具有映射关系的数据。实际问题中,元素与元素之间是有映射关系的,是动态的,你不能保证什么时候会有新的数据进来。总不能申请一个非常大的数组,那样毫无意义,而且浪费内存空间。
总体来说:
容量自增长;
- 提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量;
- 允许不同 API 之间的互操作,API之间可以来回传递集合;
- 可以方便地扩展或改写集合,提高代码复用性和可操作性。
- 通过使用JDK自带的集合类,可以降低代码维护和学习新API成本。
我所知的在java中有三种接口Iterator,ListIterator和Collection。
1、对于Iterator它可以将容器内部的取出方式按照一个统一的规则向外提供,这个规则就是Iterator接口,使得对容器的遍历操作与其具体的底层实现相隔离,达到解耦的效果。
2、对于Listlterator它对与List允许我们从俩个方向遍历,这在C中相当于链表的逆序和顺序同时存在,这个方法在有些地方非常实用。还有一个特点就是在遍历的过程中可以修改List的元素,这与C差不多。
3.对于Collection主要有List和Set俩种接口List是有序的,元素可以重复。Set是无序的,必须保证元素的唯一性。Collection是单列的,一次只能存一个元素,而Map是双列的,储存一对元素,key和value间有映射关系,可以通过key来找到value。

(引用ThinkWon的图)
三、采坑心得
说到采坑心得就不得不提这一次作业中的统计java程序中关键字的出现次数。没有明确的说出哪种情况下他算一个关键字,哪种情况不算,我试了很久才知到,这不重要。重要的是当我只剩下最后一个测试点时,我思考良久,对我的正则表达式进行了改动但这个点过了另外一个点又过不了,我当时就是一脸的......。我找了许多方法,不知不觉就三点了,不得不说打代码比打游戏还上瘾。后来知道了懒惰和贪婪匹配,可惜测试通道已经关闭,我自己测试又发现不了问题,不过我认为我已经比较完善这个代码了。
在这三次作业中有些题目给了UML图,这样我们就只需要根据它的图来设计就好,省略了好多时间。但是这样被别人限制主了也不舒服,其实根据实际情况进行修改会更好一些。
这三次作业坑倒是不多,但还是最主要的一点注意细节,你忽略一个可能就是好几个测试点没有过。第六次作业的倒数第二题:从键盘首先输入三个整型值(例如a b c),分别代表想要创建的Circle、Rectangle及Triangle对象的数量,然后根据图形数量继续输入各对象的属性值(均为实型数),数与数之间可以用一个或多个空格或回车分隔。毫无疑问用数组,因为它给了你数量,你不需要动态申请。
但是当a,b,c的和小于零时怎么办呢?我当时没有考虑到,这不是明显的数组越界了吗。细节决定成败,在代码的世界里更是如此。
四、改进建议
为了过测试点,我常常把结构完整的代码改的面目全非。有些惭愧,没有正确的去写一串代码,省略了严谨的构思过程,这导致很多时候没有考虑全面,这是一个致命的问题。
对于这三次的作业有些代码是可以深挖的,比如统计java程序中关键字出现的次数,它可以考察统计其他东西出现的次数。更甚至搜索引擎、大数据,不就是用户输入一些关键字,然后搜索引擎去匹配这些关键字在哪出现过,并且将相应结构呈现给用户。能够快速的匹配出用户想要的东西并呈现给他这是一门大学问。
对于日期的处理也可以深挖,有了日期为什么不可以加上小时,分钟甚至秒呢?如果加上这些又该如何处理,我们还可以写一个像日历一样的小程序,正确的计算天数和时间是非常重要的,可以朝着这方面改进。
五、总结
从四月份十二号的第四次作业发布开始,每隔一个星期有一次作业被发布。虽然平时都有课,但是大学课程大家都懂。这些代码填补了些许的空余时间,让我不至于去浪费时间,虽然偶尔会感觉有些累,但是看着一个一个测试点通过由绿色变为红色,这种感觉还是令我很满足的,可以清晰的看见自己的进步。
不是所有内容都会在上课学到,这样也是一件好事,人还是要自立的,自我学习是立足社会的必要技能。这些天我学习了各种字符串操作,类之间的关系,学会了看UML图,学会了正则表达式的应用。需要进一步研究的就是类之间的关系,各种关键词的使用和各种自带的类的方法。
我对这些天的学习还是很满意的,希望课程和作业更好的有机结合,也希望我在日后的学习生活中能更加刻苦。

浙公网安备 33010602011771号