抽象类与接口的异同点

不同点:

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 Doorvoid 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"关系

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐