当初学Java基础的时候很low,上课没怎么听,网课也没有一直坚持看到有关接口跟抽象类的视频,看到类与对象这一部分的视频就放弃了。太后悔了,现在对Java编程的兴趣来了,看完了狂神Javawe与Springboot的教学视频后,突然发现自己的Java基础真的是一塌糊涂!!!所以就趁着这个寒假自己看Java基础书学习一下,相当补救一下自己那糟糕的Java基础吧。希望为时未晚。
1.抽象类
1.1、抽象方法
格式:
[访问权限修饰符] abstract 返回类型 方法名([形参表]);
说明:
- 抽象方法不带方法体,它是对方法的基本说明,类似于C语言的函数原型声明。
- 即使方法形参表所在圆括号后的一对花括号内没有任何代码,此时的方法也是带方法体的普通方法。也就是说,抽象方法来拿一对花括号也不带,直接以分号结尾。
- 因为抽象方法要被子类重写,所以它可选的访问权限修饰符不能是private,且关键字abstract 不能与final或static一起修饰方法。
- 构造方法不能是抽象的。
abstract void draw();//抽象方法
1.2、抽象类
方法位于类中,若类中含有抽象方法,则该类必须以abstract 关键字声明为抽象类。相较于普通类,抽象类具有一下特点:
- 抽象类可以含零至多个抽象方法,也可以含零至多个普通方法。
- 无论抽象类是否含有抽象方法,它都不允许被实例化,即不能创建抽象类的对象,因为它描述的是抽象的概念。
- 抽象类可以含构造方法,以便创建其子类对象时由虚拟机调用,但不能通过代码显示调用抽象类的构造方法。
- 抽象类不能以final,因为抽象类存在的意义就是被子类继承。
- 若父类是抽象类,且子类不想成为抽象类,则子类必须将父类中的抽象方法重写为带方法体的普通方法,否则子类仍必须以为abstract关键字声明为抽象类。
- 在UML(统一建模语言(Unified Modeling Language,UML)是一种为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言,是非专利的第三代建模和规约语言。)中以斜体类名表示抽象类。
package cho6;
public abstract class test {
abstract void draw(); //抽象方法
void setColor(Color c){//抽象类可以含普通方法(Color为颜色类)
System.out.println("设置形状的颜色" + c.toString());
}
}
2.接口
2.1、声明接口
接口是对抽象类的进一步延伸,提供了更高级别的抽象,其常规的声明格式为:
[public] interface 接口名{
[public static final] 字段类型 字段名 = 初始值;
[public abstract] 返回类型 方法名([形参表]);
}
说明:
- 接口不允许使用除了public之外的其他访问权限修饰符,故可以省略interface左边的public。
- 接口只能包含公共抽象方法,故可以省略声明方法时的public和abstract关键字。
- 与类一样,接口也可以包含字段,但它只能是公共静态常量,故可以省略字段声明时的public、static和final关键字
- 可以将接口理解为以interface关键字修饰的特殊类,与普通类一样,接口也可以作为引用类型。
- 与抽象类一样,不允许实例化接口,即不能创建接口对象。
2.2、接口继承接口
与类的继承相似,接口之间也可以继承,其语法格式为:
[public] interface 接口名 extends 父接口1, 父接口2,···{
}
说明:
- 接口只能继承接口,而不能继承类。
- 接口的继承与类的继承相似,表达了“ is a”的逻辑。
- 与类只能继承一个父类不同,接口可以继承多个接口,彼此以逗号隔开(各父接口的顺序可任意),表达的逻辑是子接口即是一种父接口1,也是一种父接口2,···
- 若子接口继承的多个父接口定义了同名的字段,则在子接口中必须通过“父接口名.字段名”的方式显示的指定访问的哪个父接口的字段,否则会出现语法错误。
2.3、类实现接口
Java不支持多重继承,即一个类只能有一个直接父类,而现实生活中的某些具体事物是属于其他多种食物的。例如,粉笔即是教学用具,又是能画画的东西(假设教学用具和能画画的东西之间不存在继承关系),那么如何用Java来表示这样的逻辑呢?答案在于接口——Java通过接口变相实现多重继承。
一个类只能继承一个父母,可以实现多个接口。
为了区别类继承类、接口继承接口,Java将类“继承”接口描述为类实现接口。该类就被成为该接口的实现——以implemens(实现)为关键字表示,其语法格式为:
[修饰符] class 类名 [extends 父类名] [implemens 接口名1,接口名2···]{
//类体
}
说明:
- 类可以实现零至多个接口,接口名彼此间以逗号隔开 ,且与顺序无关。
- 若类A实现了接口B,则称类A为接口B的实现类。一个接口可以有多个实现类。
- 类必须重写其实现所有的接口的所有抽象方法,否则该类必须被声明为抽象类。
- 与继承类有所不同,实现接口真正表示的是“like a”的逻辑。
- 接口实现类的类名经常采用“接口名+Impl”的命名方法。如User——>UserImpl。
一个接口可以有多个不同的实现类,接口充当了这些实现类的 公共协议或契约。接口清晰分离了软件系统的功能说明(即接口)和功能实现细节(即实现类),降低了二者的耦合程度——当功能的实现细节发生变化时,只要接口不变,则调用接口的代码也不需要做任何修改,使得软件系统符合OCP原则(开闭原则(Open Closed Principle)是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的、灵活的系统。),从而提升系统的可拓展性。
2.4、含默认方法的接口
为了能够在不影响已有实现类的前提下为接口增加新行为,从JDK8开始,接口允许包含带方法体的普通方法——默认方法,其语法格式为:
default [public] 返回类型 方法名 ([形参表]){
//方法体
}
说明:
- 是否带有public关键字的效果是一样的,故通常省略。
- 接口I的子接口及实现类将自动拥有I的默认方法m,子接口及实现类也可以将m重新声明为不带方法体的抽象方法。
- 可以将default关键字替换为static ,以便直接通过接口名调用该方法。
- 接口可以同时含有任意个数的抽象方法和默认方法。
注意:
若类C实现了多个接口 ,且这些接口中定义了相同的、以default修饰的默认方法m,则类C必须重写方法m(可通过“接口.super.m的形式调用指定接口的m方法),否则调用leiC对象的m方法时,无法区分调用的是哪个接口的默认方法m。对于同时继承了这些接口的子接口也是如此。
3.抽象类与接口的比较
抽象类与接口是Java读现实世界中的实体进行抽象的两种机制,二者具有极大的相似性,同时也具有明显的区别。在软件系统的分析和设计阶段,到底时选择接口还是抽象类,则体现了设计是否忠实的反映了对问题域中概念本质的理解。
根据面向对象理论,所有的对象都是通过类来描述的,反之则不然——并非所有的类都是用来描述对象的,若类不能包含足够的信息用以描述具体的对象,则这样的类就应该被设计为抽象类或接口。抽象类和接口的本质是用来表征对问题域进行分析和设计所得出的抽象概念。例如,在设计一个图像编辑软件时,会发现问题域在存在着三角形、圆形等一下概念,它们是不同的,但都属于图形这一抽象概念——性质这一概念在问题域中是不存在的。正式因为抽象概念在问题域中没有对应的具体概念,故而用以表征抽象类或接口不能被实例化。下面分别从语法和设计层面来比较抽象类和接口的区别。
3.1、从语法层面
比较点 | 抽象类 | 接口 |
关键字 | default | interface |
字段 | 无限制 | 必须是public、static、final的 |
方法 | 普通方法、抽象方法 | 抽象方法、默认方法。且必须是public的 |
继承/实现 | 只能被类或抽象类继承 | 既可以被接口继承,又能别类或抽象类实现 |
多重继承 | 不支持 | 可以继承多个父接口 |
简言之,抽象类是一种功能不全的类,而接口只是方法原型声明 、默认方法和公共静态常量的集合,二者都不能够被实例化。
3.2、从设计层面
从语法层面比较抽象类和接口是一种低层次、非本质的比较,我们应该多从设计层面着眼,才能更加深刻的理解二者的本质区别。
父类和子类之间存在着“is a”关系,即父类的子类在概念本质上应该是相同的,这一点同样适用于抽象类,但对于接口来说则不然。若类A实现接口B,并不表示为“A is a B”的逻辑,而仅仅表示A实现B定义的契约(或A支持B定义的行为),从这一点看,实现接口所表示的逻辑可以称为“like a”。
补充一个面向对象设计的一个重要原则——ISP原则(接口隔离原则 Interface Segregation Principle,简称ISP。):
该原则强调:使用多个专门的小接口比使用单一的大接口更好。换句话说,站在接口调用者的角度,一个类对另一个类的依赖性应建立在最小接口上,即不应强迫调用者依赖他们不会使用到的行为,过于臃肿的接口是对接口的污染。