【问题标题】:When and where to use abstract classes and when and where to use interfaces in Java?何时何地使用抽象类以及何时何地在 Java 中使用接口?
【发布时间】:2026-01-27 23:05:01
【问题描述】:

抽象类和接口在 Java 中扮演着非常重要的角色,在某些情况下它们也有自己的重要性。它们具有某些特殊特性。它们之间存在一些可观察到的差异。让我描述其中的一些。


接口和抽象类之间的主要区别之一是抽象类永远不能被实例化,而接口却可以。

显然,它们都不能声明为 final,因为它们将被其他一些非抽象类继承。

它们都不能有静态方法。既不是具体的也不是抽象的(抽象的静态方法确实而且事实上根本不存在)。

接口永远不可能有具体的方法(具有实际实现的方法),但是抽象类也可以有具体的方法。

接口不能有构造函数,但是抽象类可以有。


这里可能会出现两个明显的问题。

一个抽象类永远不能被实例化,因为它本质上不是一个完全实现的类,而且它的完全实现要求它被其他一些非抽象类继承。如果是这样,那么抽象类不应该有它自己的构造函数,因为构造函数隐式返回它自己的类的对象,而抽象类本身不能被实例化,因此,它不应该有一个构造函数它是自己的。

接口看起来比抽象类更好也更适合使用,因为它施加的限制比抽象类施加的限制要少。在哪些非常具体的情况中,接口是有用的,而在哪些非常具体的情况中,抽象类是合适的?希望!粗体字会被考虑很多。

【问题讨论】:

  • 关于构造函数的那部分对我来说听起来不对。
  • 无法实例化接口。实现接口的类可以是(如果不是抽象的)。
  • 我担心你的一些“观察”是更多的想象。你不能实例化接口,你可以在抽象类中拥有静态方法
  • 实际上,如果您想在扩展类中强制执行常见的创建行为,您可能希望在抽象类中实现构造函数。另一方面,接口本身不能被实例化,你只能有实现类的实例。
  • 与其写下你自己问题的答案,不如编辑你的问题并删除不正确的陈述。当您提出问题时,将答案留给其他人。

标签: java interface abstract-class


【解决方案1】:

首先,您实际上在几个地方是错误的:

  • 接口和抽象类之间的主要区别之一是抽象类永远不能被实例化,而接口却可以。

错了。抽象和接口都可以匿名实例化。

  • 显然,它们都不能声明为 final,因为它们将被其他一些非抽象类继承。

是的,虽然我个人认为没有任何原因接口不能是最终的,所以它们不能被扩展,但这只是我。我明白他们为什么会做出这样的决定。

  • 它们都不能有静态方法。既不是具体的也不是抽象的(抽象的静态方法确实而且实际上根本不存在)。

抽象类可以有静态方法;对不起!

  • 接口永远不能有具体方法(具有实际实现的方法),但抽象类也可以有具体方法。

是的,这是它们之间的主要区别之一。

  • 接口不能有构造函数,但是抽象类可以有。

是的,确实如此。

现在,让我们继续您的问题:

您的第一段没有问题。那里的问题是什么?如果是“如果不能实例化抽象类,为什么还要让它们具有构造函数?”答案是让孩子们可以使用它。这是一个例子

abstract class Parent {
    String name;
    int id;
    public Parent(String n, int i) { name = n; id = i; }
}

class Child extends Parent {
    float foo;
    public Child(String n, int i, float f) {
        super(n,i);
        foo = f;
    }
}

// later
Parent p = new Parent("bob",12); // error
Child c = new Child("bob",12); // fine!

您的第二段有问题但格式错误。我认为您只是在其中缺少一个“是”... :) 答案如下:

当你想定义一个契约时,你会使用一个接口。这是一个非常具体的例子:

public interface Set<E> {
    int size(); // determine size of the set
    boolean isEmpty(); // determine if the set is empty or not
    void add(E data); // add to the set
    boolean remove(E data); // remove from the set
    boolean contains(E data); // determine if set holds something
}

所有集合的四种常用方法。

当你想定义一些行为时,你使用一个抽象类,但仍然有契约

public abstract class AbstractSet<E> implements Set<E> {
   // we define the implementation for isEmpty by saying it means
   // size is 0
   public boolean isEmpty() { return this.size() == 0; }
   // let all the other methods be determined by the implementer
}

【讨论】:

  • 接口和抽象类都不能被实例化,“匿名实例化”实际上是在实例化一个扩展抽象类或实现接口的匿名类。
  • @Daniel 我同意你的评价。我试图说明的一点是,他试图做出的改变并不存在。可以肯定地说它们没有被实例化,只是匿名做它的语法糖,或者另一方面你可以说我在那里说的话。但是说一个可以实例化而另一个不能实例化是不正确的。如果您的定义允许一个,它也允许另一个。如果它不允许一个,它就不允许另一个。这就是我试图做出的区分。你的评估是正确的。
【解决方案2】:

大多数时候,当在决定是否应该使用接口或抽象类之间进行讨论时,最终会定义如何使用它们,但并不总是为什么以及何时使用它们?此外,您可能最终使用的其他明显的具体类和实用程序类也并不总是被提出。真的,在我看来,回答这个问题的正确方法是确定你正在处理的关于域或实体对象的上下文,即你的用例是什么?

从一个非常高的层次来看,Java 由使用方法相互通信的对象(可以对现实世界中的对象建模的实体或领域对象)组成。无论如何,您希望使用接口对行为进行建模,并在继承时使用抽象类。

根据我的个人经验,我使用自上而下然后自下而上的方法来执行此操作。我开始通过查看用例并查看我需要哪些类来寻找继承。然后我看看是否有一个包含所有对象的 superClassOrInterfaceType(因为类和接口都定义了类型,为了简单起见,我将它们组合成一个词。希望它不会让它更混乱)域对象,如果我正在处理处理 subtypeClassOrInterfaceTypes 的用例,例如车辆的 superClassOrInterfaceType,例如:汽车、卡车、吉普车和摩托车。如果有层次关系,那么我定义了superClassOrInterfaceType和subtypeClassOrInterfaceTypes。

正如我所说,我通常首先要做的是为我正在处理的对象寻找一个公共域 superClassOrInterfaceType。如果是这样,我在 subtypeClassOrInterfaceTypes 之间寻找公共方法操作。如果没有,我看看是否有通用方法实现,因为即使您可能有一个 superClassOrInterfaceType 并且可能有通用方法,这些实现可能不利于代码重用。在这一点上,如果我有通用方法,但没有通用实现,我倾向于接口。但是,通过这个简单的示例,我应该在车辆子类型ClassOrInterfaceTypes 之间有一些通用方法和一些通用实现,我可以重用代码。

另一方面,如果没有继承结构,那我就从下往上开始,看看有没有常用的方法。如果没有通用的方法,也没有通用的实现,那我选择具体的类。

一般来说,如果有通用方法和通用实现的继承,并且在同一个子类型中需要多个子类型实现方法,那么我会选择一个抽象类,这很少见,但我确实使用它。如果仅仅因为继承而使用抽象类,那么如果代码更改很多,您可能会遇到问题。这在此处的示例中非常详细:Interfaces vs Abstract Classes in Java,用于不同类型的电机域对象。其中一个需要双动力电机,这需要在单个子类型类中使用多种子类型实现方法。

总而言之,作为一项规则,您希望使用接口而不是抽象类来定义行为(对象将做什么)。抽象类专注于实现层次结构和代码重用。

这里有一些更详细的链接。

Thanks Type & Gentle Class

The Magic behind Subtype Polymorphism

Maximize Flexibility with Interfaces & Abstract Classes

Interfaces vs Abstract Classes in Java

【讨论】:

    最近更新 更多