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

以下是我的回答:

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.

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

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

我哪里错了?


当前回答

简而言之,我想这样回答:

通过类层次结构进行继承意味着状态继承; 而通过接口继承则代表行为继承;

抽象类可以被视为介于这两种情况之间的东西(它引入了一些状态,但也迫使你定义一个行为),完全抽象类是一个接口(据我所知,这是c++中仅由虚拟方法组成的类的进一步发展)。

当然,从Java 8开始,事情发生了轻微的变化,但思想仍然是一样的。

我想这对于一个典型的Java面试来说已经足够了,如果你不是被编译器团队面试的话。

其他回答

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

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

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

对于差异的详尽列表..

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

为了让你能在面试中给出一个简单、合理的回答,我提供以下几点:

接口用于为一系列相关类指定API——关系就是接口。通常用于具有多个实现的情况,通过配置或在运行时选择正确的实现。(除非使用Spring,此时接口基本上就是Spring Bean)。接口通常用于解决多重继承问题。

抽象类是专门为继承而设计的类。这也意味着有多个实现,所有实现都有一些共性(在抽象类中可以找到)。

如果你想解决这个问题,那么就说抽象类经常实现接口的一部分——job就是你的了!

你很好地总结了使用和实现方面的实际差异,但没有提到意义上的差异。

接口是实现类将具有的行为的描述。实现类确保它将拥有这些可以在其上使用的方法。它基本上是类必须做出的契约或承诺。

抽象类是不同子类的基础,这些子类共享不需要重复创建的行为。子类必须完成行为,并有覆盖预定义行为的选项(只要它没有被定义为final或private)。

你会在java中找到很好的例子。util包,它包含了List这样的接口和AbstractList这样已经实现了接口的抽象类。官方文档对AbstractList的描述如下:

该类提供了List接口的框架实现,以最大限度地减少实现该接口所需的工作,该接口由“随机访问”数据存储(例如数组)支持。

我先给大家举个例子:

public interface LoginAuth{
   public String encryptPassword(String pass);
   public void checkDBforUser();
}

假设您的应用程序中有3个数据库。然后,该数据库的每个实现都需要定义上述2个方法:

public class DBMySQL implements LoginAuth{
          // Needs to implement both methods
}
public class DBOracle implements LoginAuth{
          // Needs to implement both methods
}
public class DBAbc implements LoginAuth{
          // Needs to implement both methods
}

但是,如果encryptPassword()不依赖于数据库,并且对每个类都是相同的呢?那么上面的方法就不是一个好的方法。

相反,考虑以下方法:

public abstract class LoginAuth{
   public String encryptPassword(String pass){
            // Implement the same default behavior here 
            // that is shared by all subclasses.
   }

   // Each subclass needs to provide their own implementation of this only:
   public abstract void checkDBforUser();
}

现在,在每个子类中,我们只需要实现一个方法——依赖于数据库的方法。

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

当我试图定义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.