# 接口与 Java 中的抽象类

> 原文： [https://howtodoinjava.com/oops/exploring-interfaces-and-abstract-classes-in-java/](https://howtodoinjava.com/oops/exploring-interfaces-and-abstract-classes-in-java/)

抽象类和接口是大多数 Java API 的两个主要构建块。 在本文中，我们将介绍 Java 接口和抽象类之间最明显的区别。

```java
Table of Contents

1\. Abstract classes in java
2\. Interfaces in java
3\. Abstract classes vs Interfaces
4\. When to use abstract class and when to use interfaces
5\. Java 8 default methods in interfaces
```

我将建议您先阅读[抽象](//howtodoinjava.com/object-oriented/understanding-abstraction-in-java/)，因为它是抽象类和接口背后的主要力量。

## 1\. Java 中的抽象类

用最简单的话来说，抽象类使用关键字`abstract`声明为抽象。 它可能包含也可能不包含任何抽象方法。 JVM 将抽象类标识为**不完整类**，该类尚未定义其完整行为。 声明一个类`abstract`仅能执行一件事：您无法创建该类的实例，仅此而已。

那么，为什么还要费心创建一个根本无法实例化的类呢？ 答案在于解决某些关键设计问题的方法。 我们将在本文后面介绍这一部分。

#### 1.1 抽象类的语法

```java
abstract class TestAbstractClass
{
    public abstract void abstractMethod();
    public void normalMethod()
    {
        //method body
    }
}

```

这里，我们的`TestAbstractClass`有两种方法，一种是抽象方法，第二种是常规方法。 抽象方法。 在类中使用抽象方法将迫使您将类声明为抽象本身。

#### 1.2 抽象方法

**抽象方法**是未就位实现的方法。 抽象方法给类增加了不完整性，因此编译器希望将整个类声明为抽象。

在应用程序中使用抽象类的唯一方法是扩展此类。 如果没有再次声明其`abstract`，则可以实例化其子类。 子类继承了超类的行为并且超类可以保留对子类的引用的特性在很多方面提高了抽象类的重要性。

## 2\. Java 接口

接口是大多数 Java API 的另一个基本构建块。 您将其命名为集合，I/O 或 SWT，您可以在任何地方看到它们的运行情况。

> 接口定义契约，实现类需要遵守这些契约。

这些契约本质上是未实现的方法。 Java 已经有一个未实现方法的关键字，即 `abstract`。 Java 提供了任何类都可以实现任何接口的规定，因此接口中声明的所有方法只需公开。

#### 2.1 接口语法

```java
public interface TestInterface
{
    void implementMe();
}

```

对于上述接口，任何实现类都需要覆盖`implementMe()`方法。

#### 2.2 抽象类实现接口

当实现一个接口并且不覆盖该方法时，只有一种情况，即声明实现类本身`abstract`。

```java
public abstract class TestMain implements TestInterface
{
    //No need to override implement Me
}

```

否则，必须在您的类中实现方法`implementMe()`，而没有任何其他异常。

```java
public class TestMain implements TestInterface
{
    @Override
    public void implementMe() {
        // TODO Auto-generated method stub
    }
}

```

## 3）抽象类与接口

让我们记下**抽象类和接口**之间的区别以便快速查看：

1.  接口的固有的所有方法是*公共*和*抽象*的。 您不能通过尝试减少方法的可访问性来覆盖此行为。 您甚至不能声明静态方法。 仅公开和抽象。

    另一方面，抽象类可以灵活地声明方法。 您还可以定义受保护的可访问性的抽象方法。 此外，还可以定义静态方法，只要它们不是抽象的即可。 允许使用非抽象静态方法。

2.  接口不能具有完全定义的方法。 根据定义，接口旨在提供唯一的契约。

    抽象类可以具有非抽象方法，没有任何限制。 您可以将任何关键字与非抽象方法一起使用，就像在其他任何类中一样。

3.  任何想使用抽象类的类都可以使用关键字`extends`来扩展抽象类，而对于实现接口的关键字，则使用`implements`。

    一个类只能扩展一个类，但可以实现任何数量的接口。 在 Java 中，此属性通常称为**多继承**的模拟。

4.  接口绝对是`abstract`，不能实例化； Java 抽象类也无法实例化，但是可以在存在` main()`的情况下调用。

接下来，如果我们既有抽象方法又有主类，可能会出现一个问题，我们可以尝试从`main()`调用抽象方法。 但是，此尝试将失败，因为`main()`方法始终是静态的，而抽象方法永远不能是静态的，因此您永远无法访问静态方法内的任何非静态方法。

## 4\. 何时使用抽象类以及何时使用接口

始终记住，在接口或抽象类之间选择是二选一场景，在这种情况下选择未经适当分析的任何人都会产生相同的结果。 了解当前问题后，必须非常明智地做出选择。 让我们尝试在这里添加一些智慧。

#### 4.1 抽象类的部分行为

抽象类使您可以定义一些行为。 它使它们成为应用程序框架内的优秀候选人。

让我们以[`HttpServlet`](https://docs.oracle.com/javaee/1.3/api/javax/servlet/http/HttpServlet.html "HTTP servlet")为例。 如果要使用 Servlets 技术开发 Web 应用程序，则必须继承该类。 众所周知，每个 Servlet 都有明确的生命周期阶段，即初始化，服务和销毁。 如果我们创建了每个 servlet，我们必须一次又一次地编写关于初始化和销毁​​的同一段代码。 当然，这将是一个很大的痛苦。

JDK 设计人员通过制作`HttpServlet`抽象类来解决此问题。 它具有为初始化 Servlet 和销毁 Servlet 而编写的所有基本代码。 您只需要覆盖某些方法即可在其中编写与应用程序处理相关的代码。 有道理吧！

可以使用接口添加上述特性吗？ 不，即使可以，对于大多数无辜的程序员来说，设计也将是一个地狱。

#### 4.2 契约专用接口

现在，让我们看看接口的用法。 接口仅提供契约，实现类的责任是实现提供给它的每个契约。

如果您只想**定义类**的特征，并且要强制所有实现实体实现这些特征，则该接口是最合适的。

#### 4.3 示例

我想以集合框架中的`Map`接口为例。 它仅提供规则，以及映射在实践中应如何表现。 例如它应该存储键值对，应该可以使用键等访问该值。这些规则在接口中采用抽象方法的形式。

所有实现类（例如[`HashMap`](//howtodoinjava.com/java/collections/how-hashmap-works-in-java/ "How hashmap works in java")，`HashTable`，`TreeMap`或[`WeakHashMap`](https://docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html "WeakHashMap")）都不同地实现了所有方法，因此与其他方法相比具有不同的特性。

同样，接口可用于定义职责分离。 例如，`HashMap`实现 3 个接口：`Map`，[`Serializable`](//howtodoinjava.com/java/serialization/a-mini-guide-for-implementing-serializable-interface-in-java/)和[`Clonable`](//howtodoinjava.com/java/cloning/cloneable-interface-is-broken-in-java/)。 每个接口定义了各自的职责，因此实现类选择要实现的对象，因此将提供有限的功能。

## 5\. 接口中的 Java 8 默认方法

使用 Java 8，您现在可以在接口中定义方法。 这些称为默认方法。 默认方法使您可以向库的接口添加新功能，并确保与为这些接口的较早版本编写的代码二进制兼容。

顾名思义，Java 8 中的默认方法就是默认的。 如果不覆盖它们，则它们是调用者类将调用的方法。

```java
public interface Moveable {
    default void move(){
        System.out.println("I am moving");
    }
}

```

在上面的示例中，`Moveable`接口定义了方法`move()`，并且还提供了默认实现。 如果有任何类实现了此接口，则无需实现其自己的`move()`方法版本。 它可以直接调用`instance.move()`。

```java
public class Animal implements Moveable{
    public static void main(String[] args){
        Animal tiger = new Animal();
        tiger.move();		//I am moving
    }
}

```

如果类愿意定制行为，那么它可以提供自己的定制实现并覆盖该方法。 现在将调用它自己的自定义方法。

```java
public class Animal implements Moveable{

    public void move(){
        System.out.println("I am running");
    }

    public static void main(String[] args){
        Animal tiger = new Animal();
        tiger.move();		//I am running
    }
}

```

#### 5.1）Java 8 中抽象类和接口之间的区别

如果您看到了，我们现在也可以提供带有接口的部分实现，就像抽象类一样。 因此，从本质上说，接口与抽象类之间的界线变得非常狭窄。 它们现在提供几乎相同的能力。

现在，只有一个大的区别是**您不能扩展多个类，而可以实现多个接口**。 除了这种区别之外，您还可以通过接口实现任何可能的功能，这些接口可以使抽象类成为可能，反之亦然。

希望您在 Java 中找到了有关**接口和抽象类的足够信息**。

学习愉快！