抽象类 VS 接口
引言
接口和抽象类是面向对象编程(OOP, Object Oriented programming)中两个绕不开的概念,二者相似而又有所不同。接下来,我们来了解二者的概念并比较它们的异同。
什么是抽象类型?
抽象类是一种特殊的类,该类不能被实例化。抽象类的存在就是为了被继承,即抽象类可以被其它类继承但不能被实例化。那么,我们为什么需要一个无法被实例化的类呢?这样做的优点是,通过抽象类我们制定了一份强制所有子类必须遵守的合约,使所有子类有着一致的层次结构。抽象类提供了一种规范用于规定子类如何进行工作,子类可根据自身情况来重写抽象类中的抽象成员(及其它可被重写的成员)以满足自身需求。
抽象类作为一个基类,可以包含已实现的成员,同时应至少包含一个抽象成员,否则就没必要使用抽象类了。如果一个抽象类中仅仅包含抽象方法,那么这时抽象类就和接口很像了。
什么是接口?
接口中不能包含任何被实现的成员,即接口中只能包含成员的签名。如,没有方法体的方法、只包含访问器关键字(set、get)的属性等。和抽象类类似,接口也是一份合约。C#中,接口和抽象类的主要区别是,类可以实现多个接口,但只能继承一个(抽象)类。
比较异同
特征 | 接口 | 抽象类 |
---|---|---|
是否支持多继承 | 支持 | 不支持 |
默认实现 | 接口中不能包含任何已实现的成员 | 抽象类中可以包含已实现(非抽象)的成员 |
访问修饰符 | 接口成员默认是公共(public)的,不再允许被任何访问修饰符修饰 | 抽象类成员可以被访问修饰符(不能是private)修饰 |
核心 VS 辅助 | 接口多用于定义(辅助性的)能力 | 抽象类多用于定义相同类型(这里类型不是数据类型的意思,解释见下文)子类所共有的一些特征 |
若只提供一些方法上的约束,建议使用接口 | 如果子类属于同一类型,且具有相同的行为或状态,建议使用抽象类提供约束 | |
寻找成员速度 | 相比抽象类较慢 | 相比接口更快 |
成员变动的影响 | 如果接口成员发生改动,则所有实现类都要进行改动 | 若向抽象类中添加非抽象成员,我们可以给该成员提供默认实现,这样子类就无需发生变动 |
允许包含的抽象成员 | 属性、方法、事件、索引器(这四类本质上都是方法) | 属性、方法、事件、索引器 |
是否允许定义字段 | 不允许 | 不允许定义使用abstract修饰的字段 |
抽象类是对子类的抽象,即将子类中的公共部分提取出来,放到一个特定的类中。抽象类是一份合约,用于为同一类型(这里类型不是指数据类型,而是逻辑上的划分,如人和猫都是动物)的子类提供约束。
接口也是一份合约,但接口多用对能力的定义,即用于指定实现类能做哪些事儿。

人和猫,都属于动物这个大类,我们可以抽象出二者的公共部分。如,年龄、体重、吃、会叫(用于形容人不太友好)等作为一个抽象类的成员。
abstract class Animal
{
public abstract int Age { set; get; }
public abstract float Weight { set; get; }
public abstract void Call();
//默认实现
public virtual void Eat()
{
Console.WriteLine("猫粮......");
}
} class Person : Animal
{
public override int Age { set; get; }
public override float Weight { set; get; } public override void Eat()
{
Console.WriteLine("鱼香肉丝盖饭......");
} public override void Call()
{
Console.WriteLine("雷好啊......");
}
} class Cat : Animal
{
public override int Age { set; get; }
public override float Weight { set; get; } public override void Call()
{
Console.WriteLine("喵喵喵......");
}
}
那么,只要继承Animal类的类都应属于动物这个大类,汽车就不应该继承Animal类。
此外,人和猫相比,人会制作工具而猫不行,那么制作工具的技能就是人所特有的,这时可以定义一个接口提供对制作工具这项技能的约束,然后让Person类实现该接口。
interface ICreatTool
{
void CreateTool();
} class Person : Animal, ICreatTool
{
public override int Age { set; get; }
public override float Weight { set; get; } public override void Eat()
{
Console.WriteLine("鱼香肉丝盖饭......");
} public override void Call()
{
Console.WriteLine("雷好啊......");
} public void CreateTool()
{
Console.WriteLine("造台MAC......");
}
}
再如,人、车、猫三者都可以跑,那么也可以定义个接口用于对跑这项技能提供约束。
interface IRun
{
void Run();
} class Person : IRun
{
public void Run()
{
Console.WriteLine("11路公交......");
}
} class Cat : IRun
{
public void Run()
{
Console.WriteLine("四蹄奔腾......");
}
} class Car : IRun
{
public void Run()
{
Console.WriteLine("四轮......");
}
}
小结
用简单的话概括接口和抽象类的异同:
抽象类和接口都是一种约束,这种约束使我们的代码有更好的层次结构,特别是在多人协同开发时(若每个人都按照自己的习惯来,对整个开发团队而言,开发成本不知要提高多少)。
抽象类是对相同类型(不是数据类型)子类公共部分的抽象(约束),接口是对能力的一种约束。
此外,建议大家读一读文末给出的这篇文章,本人读完收益颇多,本文中的表格部分引自该文。
参考文章
抽象类 VS 接口的更多相关文章
- java抽象类和接口
面向对象设计过程中重要的一点是如何进行抽象,即把"问题空间"中的元素与"方案空间"中的元素建立理想的一对一的映射关系.抽象类和接口便是抽象过程中的产物. ...
- php中抽象类与接口的概念以及区别
php里面的接口类,抽象类到底有什么用呢? 刚接触php的时候,觉得这个东西没什么用,其实这些东西还是有一定的作用的,下面我就简单的说说. 1.php 接口类:interface 其实他们的作用很简单 ...
- Java 抽象类与接口
接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 抽象类与接口是 Java 语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予 Java 强大的面向对象的能力.他们两者之间对 ...
- 浅谈我对C#中抽象类与接口的理解
C#中的抽象类与接口有些相似,初学者很容易混淆,今天就让我来谈谈对二者的理解. 首先我们得明确二者的含义,分述如下: 如果一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个 ...
- Atitit 深入理解抽象类与接口 attilax总结
Atitit 深入理解抽象类与接口 attilax总结 1.1. 主要区别接口侧重于动作抽象..抽象类是属性名词抽象..1 1.2. 抽象层次类>>抽象类>>接口1 1.3. ...
- luogg_java学习_07_抽象类_接口_多态学习总结
这篇博客总结了半天,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 , 抽象 一种专门用来做父类,被继承的. (模板) 格式: abs ...
- PHP中抽象类,接口定义
这里先介绍接口,因为在我最近看的好几本php工具书中都没有提到抽象类. 本人也觉得,在理解了接口后抽象类也非常好理解. 例子代码随便写了一下.例子代码是很ok的,测试过了不会报错,懒得看代码的筒靴们看 ...
- 0026 Java学习笔记-面向对象-抽象类、接口
抽象方法与抽象类 抽象方法用abstract修饰,没有方法体部分,连花括号都不能有: 抽象方法和抽象类都用abstract修饰 包含抽象方法的类一定是抽象类:但不包含抽象方法的类也可以是抽象类 不能创 ...
- Java抽象类与接口的区别
很多常见的面试题都会出诸如抽象类和接口有什么区别,什么情况下会使用抽象类和什么情况你会使用接口这样的问题.本文我们将仔细讨论这些话题. 在讨论它们之间的不同点之前,我们先看看抽象类.接口各自的特性. ...
随机推荐
- JavaScript function函数种类
本篇主要介绍普通函数.匿名函数.闭包函数 目录 1. 普通函数:介绍普通函数的特性:同名覆盖.arguments对象.默认返回值等. 2. 匿名函数:介绍匿名函数的特性:变量匿名函数.无名称匿名函数. ...
- 23种设计模式--工厂模式-Factory Pattern
一.工厂模式的介绍 工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例 ...
- jQuery学习之路(3)- 事件
▓▓▓▓▓▓ 大致介绍 jQuery增加了并扩展了基本的事件处理机制,不但提供了更加优雅的事件处理语法,而且极大地增强了事件处理能力 ▓▓▓▓▓▓ jQuery中的事件 ▓▓▓▓▓▓ 加载DOM 在j ...
- javascript单元测试框架mochajs详解
关于单元测试的想法 对于一些比较重要的项目,每次更新代码之后总是要自己测好久,担心一旦上线出了问题影响的服务太多,此时就希望能有一个比较规范的测试流程.在github上看到牛逼的javascript开 ...
- bootstrap-fileinput 简单使用
bootstrap-fileinput 是一款图片/文件上传 bootstrap 插件,简单示例代码: <!DOCTYPE html> <html> <head> ...
- 源码分析netty服务器创建过程vs java nio服务器创建
1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...
- 香蕉云APP,2016下半年开发日记
2016-6-17 数据库设计不应该过多依赖范式,适度的冗余可以加快搜索速度,在服务器的配置还可以的情况下,可以采用冗余来解决查找慢的问题.还一个是要选择好数据库引擎,例如 InnoDB 和 myi ...
- duang~免费的学习视频来啦:学霸君之全栈测试
学霸君向童鞋们推荐一款 同名学霸学习 视频教程 重点是完全免费收看学习噢!!! 今天 学霸君推荐腾讯课堂的学霸君之全栈测试 复制下方链接至腾讯课堂中报名学习 https://ke.qq.com/cou ...
- Android中的flexboxlayout布局
提到FlexboxLayout大家估计有点模糊,它是谷歌最近开源的一个android排版库,它的前身Flexbox是2009年W3C提出了一种新的布局,可以简便.完整.响应式的实现页面布局,Flexb ...
- OpenSUSE下编译安装OpenFoam
在不是Ubuntu系统下安装OpenFoam,需要采用编译安装的方式.以下以OpenSuSE为例进行编译安装. 1 软件包准备 需要下载两个程序包: OpenFOAM-4.x-version-4.1. ...