1.前言
经过了更长时间的学习,我对于JAVA的理解更深刻了,学习了更多的JAVA的具体内容,例如继承,正则表达式的用法,类图的使用方法和其必要性,封装和多态的运用。在这段时间,我同样没有松懈练习,下面的内容是对于一些题目的内容分析和作答的结果分析。我将先对于这三次的题目进行一个简要的概括:难度在逐渐上升,内容涉及到了JAVA面向对象编程的基本操作,包括封装继承多态,类图的理解和使用,正则表达式的用法。
2.设计与分析
这几次作业,存在循序渐进的难度,以同一个类型的题目,不断地细化,以锻炼面向对象程序设计能力,接下来将对这几次作业循序渐进的内容进行具体分析:
(1)题目集4(7-2)和题目集5(7-4)两种日期类聚合设计的优劣比较:
先是题目集4(7-2)的类图
先对需求进行一个简单分析:
这里先将年月日分成了三个类,对于年月日本身的修改方法被放入了各个类里面。
同时,month有修改year的方法,day有修改month的方法,dateutil有修改day的方法。各个类还包括:加一减一方法,范围判断方法,日期复位,校验数据是否合法等。
这使得各个内容具有较强的修改性和复用性。
这几个类的联系,呈现一种链表式的结构,month包含year,day包含month,dateutil包含day。
最后的一个类是DateUtil,这个类基本包括了下面问题中要求的各个内容,包括了日期是否合法的判断,日期的获取设置,判断日期是否相等,以及下n天,上n天的判断,和两个日期之间的天数。再集合到main主函数中,完成任务。
接下来贴一些简单的代码:
public DateUtil(int y,int m,int d) {
day.setValue(d);
day.getMonth().setValue(m);
day.getMonth().getYear().setValue(y);
backday.setValue(d);
backday.getMonth().setValue(m);
backday.getMonth().getYear().setValue(y);
}
//以上是在Dateutil中数据的获取和初始化构造,可以很明显看出来,对于越往早的数据操作,其承载的类越多,越复杂,调用的层数越多。
DateUtil predate = new DateUtil(in.nextInt(),in.nextInt(),in.nextInt());
//在Main之中只有这一个构造方式,简化了上层建筑对于下层修改的操作。
接下来是与上文对比的题目集5(7-4)的类图
先对需求进行一个简单分析:
这里先将年月日分成了三个类,对于年月日本身的修改方法被放入了各个类里面,包括:加一减一方法,范围判断方法,日期复位,校验数据是否合法等。
这使得各个内容具有较强的修改性和复用性。
在这其中,呈现一种集中式的结构,dateutil类集合了所有的内容,包括各个month,year,day类的getter,setter方法。
包括了日期是否合法的判断,日期的获取设置,判断日期是否相等,以及下n天,上n天的判断,和两个日期之间的天数,判断日期月份先后,和输出格的认定。
再集合到main主函数中,完成任务。
接下来贴一些简单的代码:
private Day day = new Day();
private Month month = new Month();
private Year year = new Year();
//在Dateutil中直接构造三个不同日期对象。
public Day getDay() {
return day;
}
public void setDay(Day day) {
this.day = day;
}
//举两个简单的getter,setter方法例子。
对于具体的运算其实以上两种方法差别不大,无非是数据的调用方式不同而已。
将两个不同处理方式的同一问题的对比可以看出一些端详:
- 叠加链表式的处理,让关联性强的类之间更具有联通性,层层叠加式逻辑有利于将各个方法分散到各个类中,减轻了一个大的集合类的复杂程度和维护难度。
- 分散集中式的处理,让各个类独立性增强,方便了事后的维护和复用升级,各个单独类的处理简单化,一个核心的大集合类包含了各个处理方式,增强了集中制对于各个不同内容的统一调控,简化的各类调用操作。
- 两种方式的对比,很容易发现,如果要对于叠加链表类型的类关系进行修改,如果是对于上游的内容进行修改,下游与之相关的内容将会不得不修改,这就导致了潜在的bug可能。
- 相应的分散集中制没有这类问题,但是集中的那个类过于赘余,复杂程度和体量远大于各个分散类,增加了维护和修改的难度。
(2)题目集4(7-3)、题目集6(7-5、7-6)三种渐进式图形继承设计的思路与技术运用(封装、继承、多态、接口等)分析:
先放出题目集4(7-3)的题目截图:
先对需求进行一个简单分析:
有一个父类shape存在一个求图形面积的公有方法,方便后续重载。
再将后续类分成两类:
圆circle类,继承父类shape,有私有类型的double的半径radius属性,再重写父类的求面积方法。
同样的rectangle类,继承父类shape,有私有类型的double的width和length,同样重写父类的求面积方式。
再往下,有类box继承rectangle类,,在原有属性方法的基础上,重写父类求面积方法,改为求球面面积,同时添加求球的体积方法。
同样的有类ball继承recrangle类,添加私有属性height,重写父类求面积方法,改为求长方体面积,同样添加求长方体体积的方法。
接下来是一些细节方面的处理:
- 对于输入的每个数据进行一个合法判断,这个判断可以放在shape之中,随着不断地继承分配到各个类中,方便了后续的处理。
- 将各个属性私有化,并且提供相应的getter,setter方法。
- 对于各类再重新构造方法。
接下来贴一些简单的代码:
class Shape{
public Shape() {
System.out.println("Constructing Shape");
}
public double getArea() {
return 0.0;
}
}
//对于最基本的shape往往只需要最基本的内容。
class Circle extends Shape{
private double radius = 0.0;
public Circle() {
System.out.println("Constructing Circle");
}
public Circle(double radius) {
this.radius = radius;
System.out.println("Constructing Circle");
}
public double getArea() {
return radius * radius * Math.PI;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
}
//特举一个继承的例子,可以看到关于这个类特有的方法都在这个类中实现。这里已经体现了上文中所需要的封装继承和多态。
再放出题目集6(7-5)的题目截图:
先对需求进行一个简单分析:
有一个父类shape有一个求图形面积的公有方法,同时提供数据检验和字符串转化,方便后续重载。
再分成三类:圆circle类,继承父类shape,有私有类型的double的半径radius属性,再重写父类的求面积方法。
rectangle类,继承父类shape,有私有类型的double的width和length,同样重写父类的求面积方式。
Triangle类,继承父类shape,有私有类型的double的side1,side2,side3,同样重写父类的求面积方式。
接下来贴一些简单的代码:
abstract class Shape{
public abstract double getArea();
public abstract boolean vaildate();
public abstract String toString();
}
//这里我对于父类shape的处理是将其设置为抽象类,因为其各个方法在不同的类中有不同的重写,而且其中的每个方法继承它的子类都会用到,所以这里设置为抽象类。
class Circle extends Shape{
private double radius;
private double area;
public Circle(double radius){
this.radius = radius;
this.area = radius*radius*Math.PI;
}
@Override
public double getArea() {
// TODO 自动生成的方法存根
return this.area;
}
@Override
public boolean vaildate() {
// TODO 自动生成的方法存根
return false;
}
@Override
public String toString() {
// TODO 自动生成的方法存根
return null;
}
}
//这是其中一个Circle类 的表示,我将面积的计算直接放在了构造中,这里其实有一个问题,虽然有相关类判断输入数据的合法性,我却没有加入判断功能。
//同样的,这里体现了JAVA中的封装继承和多态。
再放出题目集6(7-6)的题目截图:
先对需求进行一个简单分析:
在这个题目中,各个图形类较为独立了。
但是因为有一个共同的求面积方法,所以有一个getarea接口为两边提供方法。
圆circle类,继承父类shape,要求我们提供两种构造方式,一种无参数,一种包含参数构造,有私有类型的double的半径radius属性,同时有相应的getter,setter,再重写getarea接口的求面积方法。
rectangle类,继承父类shape,要求我们提供两种构造方式,一种无参数,一种包含参数构造,有私有类型的double的width和length,同时有相应的getter,setter,再重写getarea接口的求面积方法。
再集合到main中完成操作。
接下来贴一些简单的代码:
interface GetArea {
public double getArea();
}
//简单的接口。
class Circle implements GetArea{
private double radius;
private double area;
public double getRadius () {
return 1;
}
public void setRadius(double radius) {
}
public Circle() {
}
public Circle(double radius) {
this.radius = radius;
this.area = radius*radius*Math.PI;
}
public double getArea() {
return this.area;
}
}
//简单的对于接口的实现,同时有一定内容的封装效果。
将这几个对同一问题的多个处理方式进行对比我们不难看出以下几点:
1.对于各个不同类都有的共通的属性,可以有多重方法进行处理。
例如在题目集4(7-3)中,各个类之间有一种延展的性质,以一个最基本集合为父类,再往下进行内容的延展,每个类都做自己的各类方法。
这样保证了各个类之间的独立性,但是当要进行全体修改的时候,每个类都要添加部分内容,工作繁杂。
2.再就是题目集6(7-5、7-6)。
先是7-5很容易发现的是,父类把各个子类中所需的方法先进行了规定,后面的子类只需要重载实现就可,同时各个类也有自己独立的方法。
问题是当我多个不同集合有重合点的时候,继承单个父类的缺点就会显示,因为只能继承一个父类,所以当一个类同时有两方面的需求的时候不能做到我们所需的加强逻辑性方便后续操作处理的效果。
3.接下来是先是7-6,我们知道一个类可以接上多个接口,这就解决了我们上面所说的涉及到多个基本集合内容的类如何同时处在多个集合中的问题。
这里各个类都用的getarea方法放在接口之中。
第二是接口同时具有一定父类的特点,可以有一个最基本集成作用,方便了后续类中各个方法的实现。问题依旧是各个类之间独立性较强,如果要进行统一的修改会较为复杂,不过这个可以通过各类的接口解决。
(2)对三次题目集中用到的正则表达式技术的分析总结:
先放出三次题目集中对于字符串操作要求的题目:
我们可以看到题目要求逐渐复杂,从简到难。
其实输入内容的校验一直是一道难题,我们之前愚笨的字符串单个字符循环检索的方式已经逐渐不能适应越来越复杂的环境和题目。
正则表达式能做到从简单的判断字符长度,到判断不同的输入字符是数字或者是字母,和一些格式的要求。
正则表达式主要分为两部分:一部分使用特殊的方式表示一定格式的字符串,另一部分是有一些方法能进行简单的字符操作,例如有一些方法进行替换分割插入操作。
接下来贴一些处理的代码:
7-1
String Gui = "^[1-9]\\d{4,14}";
7-3
String Yan = "\\w{4}";
7-4
String Dai = "2020(1[1-7]|61|7[1-3]|8[1-2])(0[1-9]|[1-3][0-9]|40)";
总结:
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。方便了字符串校验和调整的操作,提供了更多的方法操作字符串,降低了程序的复杂度等好处。
(3)对于题目集5(7-4)中Java集合框架应用的分析总结:
对于题目的需求进行简单分析:
首先有一个集合的关键字库进行内容对比,再就是对于输入内容的分析,这里十分复杂,我先单个分析:
- 判断注释内容以及字符串内容。
- 对于关键字的数目存储和排序。
- 对于非法输入的判断。
- 对于结束的判断(如果exit在某个注释或者摸个字符串内,不能退出)
接下来贴一些处理的源代码:
static String[] KEYWORDS = { "abstract", "assert","boolean", "break", "byte","case", "catch", "char", "class", "const","continue", "default", "do", "double", "else","enum", "extends", "final", "finally", "float","for", "false", "goto", "if", "implements", "import","instanceof", "int", "interface", "long", "native","new", "null" , "package", "private", "protected","public","true", "return", "strictfp", "short", "static","super", "switch", "synchronized", "this", "throw","throws","transient", "try", "void", "volatile", "while"};
//54个关键字
public static void matchKeywords(String line) {
String stu = "(\".?\")";
line = line.replaceAll(stu, " ");
String[] wordList = line.replaceAll("[\\t\\,\\{\\;\\.]", " ").split(" ");
for (int i = 0; i < wordList.length; i++) {
for (int j = 0; j < 53; j++) {
if (wordList[i].equals(KEYWORDS[j])) {
int count = (int) keywords.get(KEYWORDS[j]);
keywords.put(KEYWORDS[j], count + 1);
}
}
}
}
//matchKeywords负责抓取关键字。
3.踩坑心得?(踩坑后悔录!)
我就先从上往下简单的分析一波,首先是题目集4(7-2)和题目集5(7-4)两种日期类聚合设计。
先简单说一下自己的弊端,没有输入的非数字的判断,也就是说我这里默认输入的是数字,当输入非数字的时候会直接弹出。
第二是关于实际的运算操作,一些本可以简化的操作被我复杂化,甚至于每次的日期数据操作我都在本类重新写了一个方法来完成,本来这些操作都是在原函数继承的方法内完成的,这是一个弊端。
第三是实际有一些操作被我直接放入main中,这里没有体现面向对象编程的特性,是我的疏忽。
然后是题目集4(7-3)、题目集6(7-5、7-6)三种渐进式图形继承设计的思路与技术运用
这里实际上代码的复用性还蛮高得,基本每个类进行简单的修改后都可以复用到这几道题中。
还是说一下自己的问题吧,首先是类与类之间的关系不明确,也就是说在实际操作过程中并没有完全根据类图实现操作。
第二是仍然没有做输入的错误判断,仍然默认输入数字,当输入非数字的时候会直接弹出。
第三是实际有一些操作被我直接放入main中,这里没有体现面向对象编程的特性。
接下来是关于字符串处理的心得:
很简单的道理,我刚开始是拒绝学习正则表达式的,既然可以用简单的方式解决问题,为什么要用复杂的方式解决呢(至少我看来是这么回事)。
打个比方
这里如果是运用之前的办法来操作,就是先判断字符串长度,然后提取每一个位置的字符。
第一二个位置字符进行一波判断
第三四个再判断
第五位判断
往下这样完成任务,看起来并不复杂,但这里就要写上几十行的if判断语句。
然后我不堪其扰,单一个8位数的数字判断就要我写上几十行if代码我是接受不了的,所以我就去学习了正则表达式,然后一看,真香。
就是上面这个问题,用正则表达式来表达就是:
String Dai = "2020(1[1-7]|61|7[1-3]|8[1-2])(0[1-9]|[1-3][0-9]|40)";
就一行就完成了几乎所有的判断,可以说是非常方便了。
4.改进建议:
对于我自己的代码,其实核心操作应该是将各个内容分散完成。做到当出现和之前有关的方法时,可以通过重写做到一种类型的复用。
5.总结:
先是对本阶段三次题目集的综合性总结,在这几次作业中,我学习到了JAVA基本操作的实现,比如封装继承和多态。虽然在实际运用上仍然有一定问题,但实际上在一次又一次的作业中已经在逐渐完成。接下来是对于类与类之间的关系运用,我发现有链表式的类处理方式,集合与集合之间的类处理关系,两种操作各有好处各有劣势。接下来是关于正则表达式的内容,正则表达式简化了关于字符串的操作,方便了字符串的查找判断替换。接下来会有更多的JAVA学习内容。