Blogs 2
一、前言
知识点汇总:
- 父类和子类:
继承使得你可以定义一个通用的类(即父类),之后继承该类为一个更特定的类(即子类)。使用类来对同一对象建模。不同的类可能会有一些共同的行为和特征,可以在一个通用类中表达这些共同之处,并被其他类所共享。可以定义特定的类继承自通用类。这些特定的类继承自通用类中的特征和方法。在java术语中,如果类C1继承自另一个类C2,那么就将C1称为子类,C2称为父类。子类从它的父类中继承可访问的数据域和方法,还可以添加新的数据域和方法。
例如:
public class Rectangle extends GeometriocObject
关键字extends 告诉编译器Rectangle 类继承自GeometriocObject类,也就是继承了GeometriocObject类中的各个方法。
*关于继承应注意以下几个关键点:
①和习惯性说法不同,子类并不是父类的一个子集。实际上,一个子类通常比他的父类包含更多的信息和方法。
②父类中的私有数据域在该类之外是不可访问的。因此,不能在子类中直接使用。但是,如果父类中定义了公共的访问器/修改器,那么可以通过这些公共的访问器/修改器来访问/修改它们。
③不是所有的“是一种”关系都该用继承来建模。
④继承是用来为“是一种”关系建模的,一个父类和1它的子类之间必须是“是一种”关系。例如,尽管Person类和Tree类可以共享类似height和weight这样的通用特性,但是从Person类继承出Tree类是毫无意义的。
⑤如果使用extends关键字来定义一个子类,它只允许一个父类。
2.使用super关键字
关键字super指代父类,可以用于调用父类中的普通方法和构造方法。
①调用父类中的构造方法:构造方法用于构建一个类的实例。不同于属性和方法,父类的构造方法不会被子类继承。它们只能使用关键字super从子类的构造方法中调用。
语法:super( )或者 super(arguments);
语句super( )或者 super(arguments)必须出现在子类构造方法的第一行,这是显式调用父类构造方法的唯一方式。
*要调用父类的构造方法就必须使用关键字super,而且这个调用必须是构造方法的第一条语句。在子类中调用父类构造方法的名字会引起一个语法错误。
②调用父类中的普通方法:关键字super不仅可以引用父类的构造方法,也可以引用父类的方法。语法如下:
super.方法名(参数);
3.方法重写:
有时,子类需要修改父类中定义的方法的实现,这称为方法重写。
*以下几点值得注意:
①重写的方法必须与被重写的方法具有一样的签名,以及一样或者兼容的返回类型。兼容的含义是重写方法的返回类型可以是被重写方法的返回类型的子类型。
②仅当实例方法可访问时,它才能被重写。因为私有方法在它所处的类本身以外是不能访问的,所以它不能被重写。如果子类中定义的方法在父类中是私有的,那么这两个方法完全没有关系。
③与实例方法一样,静态方法也能被继承。但是静态方法不能被重写。如果父类中定义的静态方法在子类中被重新定义吗,那么在父类中定义的静态方法将会被隐蔽。可以使用语法“父类名.静态方法名”调用隐蔽的静态方法。
4.方法重写与重载
方法重写发生在具有继承关系的不同类中;方法重载发生在同一个类中,也可以发生在具有继承关系的不同类中。方法重写具有同样的签名;方法重载具有同样的名字但是不同的参数列表。
为了避免错误,可以使用一种特殊的Java语法,称为重写标注,在子类的方法前面放一个@Override。例如:
public class Circle extends GeometricObject{
@Override
public String toString( ){
return super.toString( ) + “\nradius is ”+ radius;
}
}
该标注表示被标注的方法必须重写父类的一个方法。
5.多态
所谓的“多态”,简单的理解就是对象在不同情况下的不同表现,具体体现在定义和功能两个方面,简单的总结一下,多态可以用“三个定义和两个方法”来总结。三个定义分别是父类定义子类构建、接口定义实现类构建和抽象类定义实体类构建,而两个方法分别是方法重载和方法重写。
二.设计与分析
(一)两个日期类聚合设计的优劣比较

图一(聚合一)

图二(聚合二)
应用程序共测试三个功能:
- 求下n天
- 求前n天
- 求两个日期相差的天数
输入格式:
有三种输入方式(以输入的第一个数字划分[1,3]):
1 year month day n //测试输入日期的下n天
2 year month day n //测试输入日期的前n天
3 year1 month1 day1 year2 month2 day2 //测试两个日期之间相差的天数
输出格式:
- 当输入有误时,输出格式如下: Wrong Format
- 当第一个数字为1且输入均有效,输出格式如下:
year1-month1-day1 next n days is:year2-month2-day2
- 当第一个数字为2且输入均有效,输出格式如下:
year1-month1-day1 previous n days is:year2-month2-day2
- 当第一个数字为3且输入均有效,输出格式如下:
The days between year1-month1-day1 and year2-month2-day2 are:值
输入样例1:
在这里给出一组输入。例如:
3 2014 2 14 2020 6 14
输出样例1:
在这里给出相应的输出。例如:
The days between 2014-2-14 and 2020-6-14 are:2312
输入样例2:
在这里给出一组输入。例如:
0 2000 5 12 30
输出样例2:
在这里给出相应的输出。例如:
Wrong Format
分析:以上两个日期类实现的功能一致,但在聚合设计上存在差异。
聚合一中类Day被聚集于类DateUtil,类Month被聚集于类Day,类Year被聚集于Month。它们之间互相关联,层层聚集,整体呈直线相关,各个聚集类之间互不干扰。只能通过在一个相关的聚集类中来调用另一个被聚集类,即直线型的方法链,比如说类DateUtil调用类Day,类Day调用类Month,类Month调用类Year,无法越界调用,更不能直接在一个构造方法中直接调用三者得到想要的结果。聚合二中三个类Day、Month、Year全部被聚合于同一个类DateUtil中,呈现了一种主要集中的聚合关系,使得类Day、Month、Year之间相互独立,互不相干,没有直接的聚合关系可以调用,而是仅仅可以由一个大类DateUtil施行对类Day、Month、Year的整体调用,方法链也不是直线相关的了,变成了三分天下最后集中统一的形势。下面对两个聚合的源码圈一下复杂度:


聚合一


聚合二
通过比对两个聚合的SourceMonitor的生成报表可以看得出两个聚合的复杂度并无太大的差异。但个人认为还是第二个聚合比较好一点。第二个虽然耦合性低一点,但年月日一般是个整体,相对于好处理一点,但两个聚合的具体应用还得看场景。
(二)三种渐进式图形继承设计的思路与技术运用

图一

图二

图三
以上是三个关于图形的渐进式的继承与多态设计的题目。
第一题(图一)使用了this及super关键字,构造了方法链并且调用方法链,保留小数格式时采用了String.format,重写方法使用了标注@Override;类Shape,返回求图形面积的公有方法public double getArea()来求图形面积,类Circle,继承自Shape,重写父类继承来的求面积方法,求圆的面积,类Rectangle,继承自Shape,重写父类继承来的求面积方法,求矩形的面积,类Ball,继承自Circle,其属性从父类继承,重写父类求面积方法,求球表面积,类Box,继承自Rectangle,除从父类继承的属性外,再定义一个属性height,重写父类继承来的求面积方法,求立方体表面积。下面来看一下它的类图:

第二题(图二)实现了图形的接口及多态性,GetArea为一个接口,无属性,只有一个GetArea(求面积)的抽象方法;Circle及Rectangle分别为圆类及矩形类,分别实现GetArea接口。题目在Main类的主方法中分别定义了一个圆类对象及矩形类对象,使用接口的引用分别调用圆类对象及矩形类对象的求面积的方法,直接输出两个图形的面积值。其中,接口的代码如下:
interface GetArea{
double getArea();
}
这个图形继承与第一个最大的不同就是加入了接口,实现了多态性。
第三题(图三)getArea()方法为抽象方法,功能为求得图形的面积;validate()方法也为抽象方法,对图形的属性进行合法性校验;toString()继承自 Object,功能为输出图形的面积信息。对于这道题的“多态”,采用的是抽象类定义、实体类构建的方式。即 Shape 为抽象类,Circle、Rectangle 及 Triangle 为实体类。 创建的各个图形对象均存储在 ArrayList类型的列表中,大概用到的方法如下有 add()、addAll()、toArray(),此外,还用到了Arrays 类和Collections 类。 对于本题的排序问题,图形的面积大小要进行升序排序,要求必须对 list 中的图形对象在 list 中进行排序,而不是对求得的面积进行排序,排序后再次求出各图形的面积并输出。这是一个比较值得注意的地方。下面我们来看一下本体的类图:

(三)正则表达式的相关应用
- 首先汇总一下有关正则的相关表达式
|
字符 说明 |
|
^ 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与"\n"或"\r"之后的位置匹配。 |
|
$ 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。 |
|
* 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。 |
|
+ 一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。 |
|
? 零次或一次匹配前面的字符或子表达式。例如,"do(es)?“匹配"do"或"does"中的"do”。? 等效于 {0,1}。 |
|
{n} n 是非负整数。正好匹配 n 次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。 |
|
{n,} n 是非负整数。至少匹配 n 次。例如,"o{2,}“不匹配"Bob"中的"o”,而匹配"foooood"中的所有 o。"o{1,}“等效于"o+”。"o{0,}“等效于"o*”。 |
|
{n,m} m 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。‘o{0,1}’ 等效于 ‘o?’。注意:您不能将空格插入逗号和数字之间。 |
|
[xyz] 字符集。匹配包含的任一字符。例如,"[abc]“匹配"plain"中的"a”。 |
|
[ ^xyz] 反向字符集。匹配未包含的任何字符。例如,"[^abc]“匹配"plain"中"p”,“l”,“i”,“n”。 |
|
[a-z] 字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。 |
|
\d 数字字符匹配。等效于 [0-9]。 |
|
\D 非数字字符匹配。等效于 [ ^0-9]。 |
|
\w 匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。 |
|
\W 与任何非单词字符匹配。与"[ ^A-Za-z0-9_]"等效。 |
常用的正则表达式
说明 正则表达式
一、校验数字的表达式
|
数字 ^ [0-9]*$ |
n位的数字 ^\d{n}$ |
|
至少n位的数字 ^\d{n,}$ |
m-n位的数字 ^\d{m,n}$ |
|
非零开头的最多带两位小数的数字 ^([1-9][0-9]*)+(.[0-9]{1,2})?$ |
带1-2位小数的正数或负数 ^(-)?\d+(.\d{1,2})?$ |
|
有1~3位小数的正实数 ^ [0-9]+(.[0-9]{1,3})?$ |
非负整数 ^\d+$ |
|
非正整数 ^((-\d+) |
|
二、校验字符的表达式
|
汉字 ^ [\u4e00-\u9fa5]{0,}$ |
|
英文和数字 ^ [A-Za-z0-9]+$ |
|
长度为3-20的所有字符 ^.{3,20}$ |
|
由26个英文字母组成的字符串 ^ [A-Za-z]+$ |
|
由数字、26个英文字母或者下划线组成的字符串 ^\w+$ 或 ^\w{3,20}$ |
|
中文、英文、数字包括下划线 ^ [\u4E00-\u9FA5A-Za-z0-9_]+$ |
|
中文、英文、数字但不包括下划线等符号 ^ [\u4E00-\u9FA5A-Za-z0-9]+$ |
|
可以输入含有^%&’,;=?$"等字符 [^%&’,;=?$\x22]+ |
|
禁止输入含有~的字符 [^~\x22]+ |
|
三、特殊需求表达式 |
|
Email地址 ^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$ |
|
域名 [a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.? |
|
InternetURL [a-zA-z]+://[^\s]* 或 ^http://([\w-]+.)+[\w-]+(/[\w-./?%&=]*)?$ |
|
手机号码 ^(13[0-9] |
|
电话号码 ^((\d{3,4}-) |
|
身份证号(15位、18位数字) ^\d{15} |
|
短身份证号码(数字、字母x结尾) ^([0-9]){7,18}(x |
|
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线) ^ [a-zA-Z][a-zA-Z0-9_]{4,15}$ |
|
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线) ^ [a-zA-Z]\w{5,17}$ |
|
日期格式 ^\d{4}-\d{1,2}-\d{1,2} |
|
中国邮政编码 [1-9]\d{5}(?!\d) (中国邮政编码为6位数字) |
|
腾讯QQ号 [1-9][0-9]{4,} (腾讯QQ号从10000开始) |
|
IP地址 \d+.\d+.\d+.\d+ (提取IP地址时有用) |
|
中文字符的正则表达式 [\u4e00-\u9fa5] |
三、使用正则表达式
使用正则表达式需要引入 java.util.regex 包,java.util.regex 包主要包括以下三个类:
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
下面就我们这几次题目集中的相关正则表达式做下分析:
比如这道校验学号的题目:
核心代码就是正则表达式:
if(Pattern.matches("([2][0][2][0])([1][1-7]{1}||[6][1]||[7][1-3]{1}||[8][1-3]{1})([0][1-9]{1}||[1-3][0-9]||[4][0])", str)) {
return true;
}
([2][0][2][0])就代表学号必须是2020开头,即必须是2020级。([1][1-7]{1}||[6][1]||[7][1-3]{1}||[8][1-3]{1})这段用于产生班级,11班——17班,61班,或者是71——73班,81——83班。
([0][1-9]{1}||[1-3][0-9]||[4][0])用于产生学号后两位,即01号——40号,每个班不超40人。
正则总结:正则表达式只是一些固有的形式或者说是语法,只需要对相关的语法表达有正确的认识,多拓展,多识记,就能在解决实际问题中得以正确的运用。上面我对常见的一些正则表达式做了下总结,一般题目涉及的正则表达式无非就是校验数据,匹配手机号,QQ号,IP地址等等。
(四)Java集合框架应用的分析
Java集合是java提供的工具包,包含了常用的数据结构:集合、链表、队列、栈、数组、映射等。Java集合工具包位置是java.util.*
Java集合主要可以划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections)。
1.Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性。
Collection包含了List和Set两大分支。
(1) List是一个有序的队列,每一个元素都有它的索引。第一个元素的索引值是0。
List的实现类有LinkedList, ArrayList, Vector, Stack。
(2) Set是一个不允许有重复元素的集合。
Set的实现类有HastSet和TreeSet。HashSet依赖于HashMap,它实际上是通过HashMap实现的;TreeSet依赖于TreeMap,它实际上是通过TreeMap实现的。
2.Map是一个映射接口,即key-value键值对。Map中的每一个元素包含“一个key”和“key对应的value”。
AbstractMap是个抽象类,它实现了Map接口中的大部分API。而HashMap,TreeMap,WeakHashMap都是继承于AbstractMap。
Hashtable虽然继承于Dictionary接口,但它实现了Map接口。
接下来,再看Iterator。它是遍历集合的工具,即我们通常通过Iterator迭代器来遍历集合。我们说Collection依赖于Iterator,是因为Collection的实现类都要实现iterator()函数,返回一个Iterator对象。
ListIterator是专门为遍历List而存在的。
再看Enumeration,它是JDK 1.0引入的抽象类。作用和Iterator一样,也是遍历集合;但是Enumeration的功能要比Iterator少。Enumeration只能在Hashtable, Vector, Stack中使用。
最后,看Arrays和Collections。它们是操作数组、集合的两个工具类。
(五)阶段性总结
通过这一阶段三次题目集的学习和练习,我对聚合的设计和应用有了更加深入的了解,对继承和多态的认识更加全面,对有关的重载和复写运用更加准确,特别是对正则表达式的学习更加深入,由最初的陌生害怕,手足无措到打开思路,深入学习,我倍感欣慰。但是在编写源码的过程中并不是一帆风顺的,那两个日期类的设计可谓是让我绞尽脑汁,殚精竭虑。那几个渐进式的图形类继承其实当理解了什么是继承和多态,怎么样去设计各个类,源码的编写也就迎刃而解。最重要的是不要畏难,要敢动手,从失败中找方法,失败的过程才是最好的经验。

浙公网安备 33010602011771号