抽象类与接口的异同点
不同点:
1.关键字不同:
① 继承抽象类的关键字是extends,而实现接口的关键字是implements;
②定义抽象类的关键字是abstract class,而定义接口的关键字是interface;
2.权限修饰不同:抽象方法可以有public、protected和default这些修饰符(缺省情况下默认为public),而接口方法只能是public ;
3.抽象类中可以有构造方法,而接口中不能有构造方法;
如果写了构造方法,编译报错:Interfaces cannot have constructors
4.抽象类中既可以有抽象方法也可以有普通方法,接口中只能有抽象方法;
注:从jdk1.8开始允许接口中出现非抽象方法,但需要使用default关键字修饰。
意义:
1.假如你接口中有很多的抽象方法,但是实现类中有时候并不需要用到那么多的抽象方法,但是又要全部重写,就会很麻烦,这时如果使用非抽象方法,就可以在实现类中重写需要使用到的抽象方法即可,大大减少了代码量。
2.当我们在接口中增加一个抽象方法时,这时所有实现接口的类中都要重写这个方法,就有牵一发而动全身的麻烦。那么如果用非抽象方法,就可以根据实际需要重写该非抽象方法。
5.抽象类中增加方法可以不影响子类,而接口中增加方法通常都影响子类。
理解:因为在抽象类中,可以定义非抽象方法,这时就不需要在子类中重写了,而接口中增加方法,这方法肯定是抽象类方法,则必须要在子类中重写。(为什么是通常呢,因为上面第四点提到,在jdk1.8开始,允许接口中出现非抽象方法)
6.抽象类中的变量可以是普通变量,接口里定义的变量只能是公共的静态的常量;
实际操作中发现定义变量时没写public static final也不会报错,因为接口只能是公共的静态常量的,编译器默认会加上,同理,定义方法时没写public abstract也不会报错
7.抽象方法可以继承单个类和实现多个接口,接口可以多继承接口;
abstract class Demo{
abstract void printfa();//抽象方法
void printfb();//编译报错,add body
void printfb() {
//非抽象方法
}
}
interface Demo1{
void printfab();
void printfa() {
//编译报错,因为接口中必须是抽象方法
}
default void printfb() {
//加上default可以了
}
}
相同点:
(1) 都可以被继承
(2) 都不能被实例化
(3) 都可以包含方法声明
(4) 派生类必须实现未实现的方法
从设计理念层面分析
转:Java中abstract class 和 interface 的解释和他们的异同点(转) - petercao - 博客园 (cnblogs.com)
abstract class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a"关系,即父类和派生类在概念本质上应该是相同的。对于interface来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。
eg:
假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型。
abstract class Door {
abstract void open();
abstract void close();
}
//或者
interface Door{
void open();
void close();
}
如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢?
方案1:
简单的在Door的定义中增加一个alarm方法
abstract class Door{
abstract void open();
abstract void close();
abstract void alarm();
}
// 或者
interface Door{
void open();
void close();
void alarm();
}
那么具有报警功能的AlarmDoor
的定义方式如下:
class AlarmDoor extends Door{
void open(){}
void close(){}
void alarm(){}
}
// 或者
class AlarmDoor implements Door{
void open(){}
void close(){}
void alarm(){}
}
这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变。
什么是ISP?
在软件工程领域,接口隔离原则(ISP)规定不应强迫客户端依赖它不使用的方法。ISP将非常大的接口拆分为更小和更具体的接口,以便客户端只需知道它们感兴趣的方法。这种缩小的接口也称为角色接口。ISP旨在使系统分离,从而更容易重构,更改和重新部署。ISP是面向对象设计的五个SOLID原则之一,类似于GRASP的高内聚原则。
大接口拆分为小接口:就是一个类实现多个接口。
不应强迫客户端依赖它不使用的方法:那就是说几个小接口要合理划分
方案2:
既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用 abstract class 方式定义;两个概念都使用interface方式定义;一个概念使abstract class 方式定义,另一个概念使用interface方式定义。显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。
如果我们对于问题领域的理解是:AlarmDoor
在概念本质上是Door,同时它有具有报 警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is-a"关系。所以对于Door这个概念,我们应该使用 abstract class方式来定义。另外,AlarmDoor
又具有报警功能,说 明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义.
abstract class Door{
abstract void open();
abstract void close();
}
interface Alarm{
void alarm();
}
class Alarm Door extends Door implements Alarm{
void open(){}
void close(){}
void alarm(){}
}
总结:abstract class表示的是"is-a"关系,interface表示的是"like-a"关系