抽象类和接口的区别

本文详细探讨了接口和抽象类的区别,包括继承、方法可见度、声明与定义、常量和属性等方面,并解释了这些差异背后的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者通过多个方面深入剖析抽象类和接口的区别,并结合经验供读者借鉴学习。

显而易见?

完全不是。笔者面试过很多人,通常问的第一个问题是关于接口和抽象类的区别。但实际上很少有程序员能给出正确的答案。

就这个问题来说,初级程序员可能都会清楚之间的区别,可能也并不一定理解其背后的原因,但其结构上的差异,特别是针对特定语言(几乎和所有的面向对象的语言一样)应该深入了解。

同时,笔者也发现其他职位候选人(有时甚至是高级职位)竟然也不知道这之间的差异,或者只知道的一个或几个。

如果只是需要了解这些内容那并不难,但这些都是面向对象的基础知识,因此想要设计良好的代码必须对其有一个深入的认识。

下面将详细介绍这些基础知识。

继承

下面将从众所周知的接口和抽象类的区别开始。这种差异是关于继承的,任何类都可以实现多个接口,但是只能扩展一个类,也只能有一个父类。

多个类扩展是一个语言特性,它存在于一些面向对象的语言。为什么呢?因为它带来的问题往往多于价值。

当一个类有许多父类时,有一个情况就是完全相同的方法会声明多个,因此必须显式地“告知”究竟需要的是哪一个。

这样的代码通常难以维护,因为对其进行的任何修改或者重构都必须小心地检查。另一方面,如果一个类需要扩展(至少)两个拥有相同方法的类,那么DRY规则显然会被破坏(因此需要从别处下手),或者说会干扰到Single Responsibility Principle(SAP)。

“如果多个类的继承如此糟糕,为什么它可以实现许多接口呢?”——如果这样的问题在你的脑海盘旋,我不得不承认这是一个绝妙的问题。

然而,答案很简单。每一个接口都是基于函数而不是一个类去实现。所以,即使实现十个不同的接口,每个包含相同的方法声明,内部也不会发生冲突。接口保证了方法的存在,而不是去说明方法的实现,这意味着,只要不违反SRP,你完全可以实现多个接口。

方法的可见度

接口中的所有方法都是public的,但对于抽象类的声明并没有这样的规则,当然不能是private。为什么不能private?因为一个抽象方法需要在子类中实现,但private无法访问子类,因此不抽象类不可能存在private属性。

接着回归主题。正如上文写道的,接口是一个函数的保证,你可以把它当作使用接口的类和实现这个接口的类之间的一个合约——保证一个特定类将实现所有声明的方法。这也是为什么这些方法必须是public的原因。因为被严格的限制到了实现上,所以其他一切都不成问题。

然而,当涉及到抽象类时并非这样。我们总是可以有不同的类组,除了这几方面基本上不同以外,其他地方几乎一样,类体的公共方法也是非常相似的。在这种情况下,可以创建protected方法来保持类之间的差异。Template Method就是一个很典型的例子。

声明和定义

接口只能包含方法声明,而抽象类还可以包含方法的定义。

接口的重点在于提供特定函数,而抽象类还在于子类实现的相似性,不仅仅是其中的函数。

常量

接口和抽象类中都可以定义常量。这是因为这些值不依赖于特定对象,对它们来说都是相同的。

属性

抽象类可以包含属性,但接口却不能。原因与声明和定义是一样的。

总结

除了说明差异,笔者也试图解释它产生的原因。这不仅是因为人们发明某个语言时的突发奇想,而是源于语言背后所支撑的理念。

### 抽象类接口区别及使用场景 #### 定义上的差异 抽象类是一种未完全实现的类,它可以包含具体方法抽象方法。如果一个类被声明为`abstract`,即使其中没有任何抽象方法,该类仍然无法实例化[^2]。而接口是对行为的一种抽象定义,它只关注对象能够做什么,而不关心具体的实现细节。 #### 继承方式的不同 Java 中的一个类只能继承自一个父类(包括抽象类),但可以同时实现多个接口。这意味着当需要让一个类具有多种能力时,通常会优先考虑使用接口而非多重继承[^3]。 #### 方法与字段的支持情况 - **抽象类**:允许存在非静态成员变量以及构造函数;还可以提供默认实现供子类调用。 - **接口**:传统上仅支持常量(隐式 `public static final` 的字段)抽象方法(隐式 `public abstract`)。然而,在 Java 8 及之后版本中引入了默认方法 (`default`) 静态方法 (`static`) 支持,这使得某些功能看起来更接近于抽象类的行为[^1]。 #### 使用场景分析 ##### 抽象类适用场合 当几个相关联的具体类共享相同的基线逻辑,并且这些共同部分适合提取出来形成统一的基础结构时,可以选择创建抽象类作为它们的父亲节点。例如构建一系列图形处理工具时,“Shape”可能适合作为基础抽象类,因为它涵盖了所有形状都应具有的基本属性如颜色填充样式等通用特征[^1]。 ##### 接口更适合的情况 如果目标在于表达某特定动作或服务的存在与否,而不是强调实体之间的层次关系,则应该倾向于采用接口形式。比如前面提到过的“飞行”的概念——并不是所有的生物都是鸟类,也不是每种机器都能称为飞机,但是只要具备飞翔的能力就可以关联到同一个 Fly 接口中去[^3]。 ```java // 示例代码展示如何定义抽象类接口 public interface Flyable { void fly(); } public abstract class Animal { public String name; public abstract void makeSound(); // 抽象方法 public void eat() { System.out.println("Eating..."); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值