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

以下是我的回答:

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接口的方法是隐式抽象的,不能有实现

从文档页:

接口是一种引用类型,类似于类,只能包含 常量、方法签名、默认方法、静态方法和嵌套类型 方法体只存在于默认方法和静态方法中。

默认的方法:

接口可以有默认方法,但与抽象类中的抽象方法不同。

默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。

当您扩展一个包含默认方法的接口时,您可以执行以下操作:

完全不要提及默认方法,这将使您的扩展接口继承默认方法。 重新声明默认方法,使其抽象。 重新定义默认方法,该方法将覆盖它。

静态方法:

除了默认方法外,还可以在接口中定义静态方法。静态方法是与定义它的类相关联的方法,而不是与任何对象相关联的方法。类的每个实例都共享它的静态方法。)

这让你更容易在你的库中组织帮助方法;

来自文档页的关于接口具有静态和默认方法的示例代码。

import java.time.*;

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second);
    LocalDateTime getLocalDateTime();

    static ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

使用下面的指导原则来选择是使用接口还是抽象类。

接口:

定义一个契约(最好是无状态的——我的意思是没有变量) 将不相关的类链接到具有一种功能。 声明公共常量变量(不可变状态)

抽象类:

在几个密切相关的类之间共享代码。它建立了一种关系。 在相关类之间共享公共状态(状态可以在具体类中修改)

相关文章:

接口与抽象类(通用面向对象)

实现和扩展:什么时候使用?有什么不同?

通过这些例子,你可以理解

不相关的类可以通过接口具有功能,但相关类通过扩展基类来改变行为。

其他回答

你的解释看起来还不错,但可能看起来像是你从课本上读的?: - /

我更关心的是,你的例子有多可靠?你是否费心去包括抽象和接口之间几乎所有的区别?

就我个人而言,我建议这个链接: http://mindprod.com/jgloss/interfacevsabstract.html#TABLE

对于差异的详尽列表..

希望它能帮助你和所有其他读者在未来的采访

当我试图在两个密切相关的类之间共享行为时,我创建了一个包含公共行为的抽象类,并作为两个类的父类。

当我试图定义Type(对象的用户可以可靠地调用的方法列表)时,我创建了一个接口。

例如,我绝不会创建一个只有一个具体子类的抽象类,因为抽象类是关于共享行为的。但是我很可能创建一个只有一个实现的接口。我的代码的用户不会知道只有一个实现。实际上,在未来的版本中可能会有几个实现,它们都是一些新的抽象类的子类,这些抽象类在我创建接口时甚至还不存在。

这似乎也有点太书生气了(尽管我在记忆中从未见过这样的说法)。如果面试官(或OP)真的想要更多关于这方面的个人经验,我早就准备好了关于界面是出于必要而进化的轶事,反之亦然。

One more thing. Java 8 now allows you to put default code into an interface, further blurring the line between interfaces and abstract classes. But from what I have seen, that feature is overused even by the makers of the Java core libraries. That feature was added, and rightly so, to make it possible to extend an interface without creating binary incompatibility. But if you are making a brand new Type by defining an interface, then the interface should be JUST an interface. If you want to also provide common code, then by all means make a helper class (abstract or concrete). Don't be cluttering your interface from the start with functionality that you may want to change.

我为工作做面试,我也会不看好你的答案(抱歉,但我很诚实)。听起来你确实读过不同之处并修改了答案,但也许你从未在实践中使用过。

一个好的解释为什么你会使用每一种,比一个精确的解释差异要好得多。雇主最终希望程序员做一些他们不了解的事情,而这些事情很难在面试中展示出来。如果你申请的是技术或文档相关的工作,而不是开发人员的职位,你给出的答案会很好。

祝你以后面试顺利。

另外,我对这个问题的回答更多的是关于面试技巧,而不是你提供的技术材料。或许可以考虑读一读。https://workplace.stackexchange.com/是做这类事情的好地方。

你的所有语句都是有效的,除了你的第一个语句(在Java 8发布之后):

Java接口的方法是隐式抽象的,不能有实现

从文档页:

接口是一种引用类型,类似于类,只能包含 常量、方法签名、默认方法、静态方法和嵌套类型 方法体只存在于默认方法和静态方法中。

默认的方法:

接口可以有默认方法,但与抽象类中的抽象方法不同。

默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。

当您扩展一个包含默认方法的接口时,您可以执行以下操作:

完全不要提及默认方法,这将使您的扩展接口继承默认方法。 重新声明默认方法,使其抽象。 重新定义默认方法,该方法将覆盖它。

静态方法:

除了默认方法外,还可以在接口中定义静态方法。静态方法是与定义它的类相关联的方法,而不是与任何对象相关联的方法。类的每个实例都共享它的静态方法。)

这让你更容易在你的库中组织帮助方法;

来自文档页的关于接口具有静态和默认方法的示例代码。

import java.time.*;

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second);
    LocalDateTime getLocalDateTime();

    static ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

使用下面的指导原则来选择是使用接口还是抽象类。

接口:

定义一个契约(最好是无状态的——我的意思是没有变量) 将不相关的类链接到具有一种功能。 声明公共常量变量(不可变状态)

抽象类:

在几个密切相关的类之间共享代码。它建立了一种关系。 在相关类之间共享公共状态(状态可以在具体类中修改)

相关文章:

接口与抽象类(通用面向对象)

实现和扩展:什么时候使用?有什么不同?

通过这些例子,你可以理解

不相关的类可以通过接口具有功能,但相关类通过扩展基类来改变行为。

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