在我的一次采访中,我被要求解释接口类和抽象类之间的区别。

以下是我的回答:

Methods of a Java interface are implicitly abstract and cannot have implementations. A Java abstract class can have instance methods that implements a default behaviour. Variables declared in a Java interface are by default final. An abstract class may contain non-final variables. Members of a Java interface are public by default. A Java abstract class can have the usual flavours of class members like private, protected, etc. A Java interface should be implemented using keyword “implements”; A Java abstract class should be extended using keyword “extends”. An interface can extend another Java interface only, an abstract class can extend another Java class and implement multiple Java interfaces. A Java class can implement multiple interfaces but it can extend only one abstract class.

然而,面试官并不满意,他告诉我这种描述代表了“书本知识”。

他让我给出一个更实际的回答,用实际的例子解释我什么时候会选择抽象类而不是接口。

我哪里错了?


当前回答

根据我的理解和我的做法,

接口就像一个规范/契约,任何实现接口类的类都必须实现抽象类中定义的所有方法(除了默认方法(在Java 8中引入))

然而,当我知道类的一些方法需要实现时,我定义了一个类抽象,而有些方法我仍然不知道实现是什么(我们可能知道函数签名,但不知道实现)。我这样做是为了在开发的后期,当我知道如何实现这些方法时,我可以扩展这个抽象类并实现这些方法。

注意:接口方法中不能有函数体,除非该方法是静态的或默认的。

其他回答

在Java中选择接口是为了避免多重继承中的菱形问题。

如果你想让你所有的方法都由你的客户端实现,你可以选择接口。这意味着你要抽象地设计整个应用程序。

如果您已经知道它们的共同点,那么您就可以选择抽象类。例如,取一个抽象类Car。在更高的级别上实现常见的car方法,如calculateRPM()。这是一种常见的方法,你让客户端实现他自己的行为,比如 calculateMaxSpeed()等。也许你会举几个你在日常工作中遇到的真实例子来解释。

这个世界上没有什么是完美的。他们可能期待的是更实际的方法。

但在你解释之后,你可以用稍微不同的方法添加这些行。

Interfaces are rules (rules because you must give an implementation to them that you can't ignore or avoid, so that they are imposed like rules) which works as a common understanding document among various teams in software development. Interfaces give the idea what is to be done but not how it will be done. So implementation completely depends on developer by following the given rules (means given signature of methods). Abstract classes may contain abstract declarations, concrete implementations, or both. Abstract declarations are like rules to be followed and concrete implementations are like guidelines (you can use it as it is or you can ignore it by overriding and giving your own implementation to it). Moreover which methods with same signature may change the behaviour in different context are provided as interface declarations as rules to implement accordingly in different contexts.

编辑:Java 8简化了在接口中定义默认方法和静态方法。

public interface SomeInterfaceOne {

    void usualAbstractMethod(String inputString);

    default void defaultMethod(String inputString){
        System.out.println("Inside SomeInterfaceOne defaultMethod::"+inputString);
    }
}

现在,当一个类将实现SomeInterface时,并不强制为interface的默认方法提供实现。

如果我们有另一个具有以下方法的接口:

public interface SomeInterfaceTwo {

    void usualAbstractMethod(String inputString);

    default void defaultMethod(String inputString){
        System.out.println("Inside SomeInterfaceTwo defaultMethod::"+inputString);
    }

}

Java不允许扩展多个类,因为这会导致“钻石问题”,即编译器无法决定使用哪个超类方法。如果使用默认方法,接口也会出现菱形问题。因为如果一个类同时实现这两个

SomeInterfaceOne and SomeInterfaceTwo

并且没有实现常见的默认方法,编译器不能决定选择哪一个。 为了避免这个问题,在java 8中必须实现不同接口的通用缺省方法。如果任何类实现了上述两个接口,它必须提供defaultMethod()方法的实现,否则编译器将抛出编译时错误。

下面是一个围绕Java 8的解释,试图展示抽象类和接口之间的关键区别,并涵盖Java助理考试所需的所有细节。

关键概念:

一个类只能扩展一个类,但它可以实现任意数量的接口 接口定义了类的功能,抽象类定义了它是什么 抽象类是类。它们不能被实例化,但在其他方面表现得像普通类 两者都可以有抽象方法和静态方法 接口可以有默认方法和静态final常量,也可以扩展其他接口 所有接口成员都是公共的(直到Java 9)

接口定义了类的功能,抽象类定义了它是什么

每罗迪格林:

接口通常用来描述一个类的能力,而不是它的中心标识,例如,一个Automobile类可能实现了可回收接口,它可以应用于许多不相关的对象。抽象类定义其后代的核心标识。如果你定义一个Dog抽象类,那么达尔马提亚的后代就是Dog,他们不仅仅是可狗的。

Pre Java 8, @Daniel Lerps的回答非常准确,接口就像实现类必须履行的契约。

现在,使用默认方法,它们更像一个Mixin,仍然执行契约,但也可以提供代码来完成这项工作。这使得接口可以接管抽象类的一些用例。

抽象类的意义在于它以抽象方法的形式缺少功能。如果一个类没有任何抽象行为(在不同类型之间变化),那么它可能是一个具体的类。

抽象类是类

下面是类的一些常规特性,这些特性在抽象类中是可用的,但在接口中是不可用的:

实例变量/非最终变量。因此…… 可以访问和修改对象状态的方法 私有/受保护成员(但请参阅Java 9的注释) 扩展抽象或具体类的能力 构造函数

关于抽象类需要注意的几点:

它们不可能是最终的(因为它们的全部目的是扩展) 扩展另一个抽象类的抽象类继承其所有抽象方法作为自己的抽象方法

抽象方法

抽象类和接口都可以有0到多个抽象方法。抽象方法:

是没有主体的方法签名(即没有{}) 在抽象类中必须用abstract关键字标记。在接口中,该关键字是不必要的 不能是私有的(因为它们需要由另一个类实现) 不能最终确定(因为他们还没有身体) 不能是静态的(因为原因)

还要注意:

抽象方法可以由同一类/接口中的非抽象方法调用 扩展抽象类或实现接口的第一个具体类必须为所有抽象方法提供实现

静态方法

抽象类上的静态方法可以直接使用MyAbstractClass.method()调用;(例如,就像一个普通的类,它也可以通过一个扩展抽象类的类来调用)。

接口也可以有静态方法。它们只能通过接口的名称来调用(MyInterface.method();)。这些方法:

不能是抽象的,即必须有一个主体(参见上面的“因为原因”) 不是默认值(见下文)

默认的方法

接口可以有默认方法,该方法必须有default关键字和方法体。这些方法只能引用其他接口方法(不能引用特定实现的状态)。这些方法:

不是静止的 不是抽象的(他们有一个主体) 不能为最终值(名称“default”表示它们可能被覆盖)

如果一个类实现了两个具有相同签名的缺省方法的接口,则会导致编译错误,这可以通过覆盖该方法来解决。

接口可以有静态的final常量

接口只能包含上面描述的方法类型或常量。

常量被假定为静态的和最终的,并且可以在实现接口的类中不加限制地使用。

所有接口成员都是公共的

在Java 8中,接口的所有成员(以及接口本身)都被假定为公共的,不能被保护或私有(但Java 9确实允许接口中的私有方法)。

这意味着实现接口的类必须定义具有公共可见性的方法(与常规规则一致,方法不能被低可见性覆盖)。

就连我也在多次面试中遇到过同样的问题,相信我,说服面试官会让你很痛苦。 如果我固有以上所有的答案,那么我需要增加一个关键点,使它更有说服力,并充分利用OO

如果你不打算在规则中进行任何修改,对于子类来说,在很长一段时间内,去接口,因为你不能在其中修改,如果你这样做,你需要在所有其他子类中进行更改,然而,如果你认为,你想重用功能,设置一些规则并使其开放修改,去抽象类。

想象一下,你使用了一个可消费的服务,或者你向世界提供了一些代码,你有机会修改一些东西,假设是一个安全检查 如果我是代码的消费者,并且在更新后的某个早上,我发现Eclipse中所有的读标记,整个应用程序都关闭了。 因此,为了避免这样的噩梦,请在接口上使用抽象

我想这会在一定程度上说服面试官……愉快的面试。

我所观察到的主要区别是抽象类为我们提供了一些已经实现的常见行为,而子类只需要实现与它们对应的特定功能。而接口只指定需要完成哪些任务,接口不会给出任何实现。我可以说它指定了它自己和实现的类之间的契约。