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

以下是我的回答:

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.

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

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

我哪里错了?


当前回答

接口就像一组公开记录的具有某种影响的基因:DNA测试会告诉我是否有它们——如果我有,我可以公开让人们知道我是“携带者”,我的部分行为或状态将符合它们。(当然,我可能还有很多其他基因,这些基因提供的特征超出了这个范围。)

抽象类就像单性别物种的死去的祖先(*):她不能被复活,但一个活着的(即非抽象的)后代继承了她所有的基因。

为了扩展这个比喻,我们假设这个物种的所有成员都活到相同的年龄。这意味着一个死去的祖先的所有祖先也必须是死的——同样,一个活着的祖先的所有后代也必须是活着的。

其他回答

接口与抽象类的基本区别在于,接口支持多重继承,而抽象类不支持。

在抽象类中,你也可以提供所有的抽象方法,比如接口。

为什么需要抽象类?

在某些情况下,当处理用户请求时,抽象类并不知道用户的意图。在该场景中,我们将在类中定义一个抽象方法,并询问用户谁扩展了这个类,请在抽象方法中提供您的意图。在这种情况下,抽象类非常有用

为什么需要接口?

比如说,我有一份工作,但我在这方面没有经验。的例子, 如果你想建造一座建筑或大坝,那么在这种情况下你会怎么做?

你将确定你的需求是什么,并根据这些需求制定一份合同。 然后打电话给投标者来构建你的项目 无论谁建造这个项目,都应该满足你的要求。但是不同厂商的构造逻辑是不同的。

这里我不关心它们是如何构造的。最终的目标是否满足我的要求,这只是我的关键点。

在这里,你的需求称为接口,构造函数称为实现者。

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

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

祝你以后面试顺利。

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

我相信面试官想要了解的可能是界面和实现之间的区别。

代码模块的接口——不是Java接口,更通用的说法是“接口”——基本上是与使用该接口的客户端代码之间的契约。

代码模块的实现是使模块工作的内部代码。通常,您可以以多种不同的方式实现特定的接口,甚至可以在客户机代码不知道更改的情况下更改实现。

A Java interface should only be used as an interface in the above generic sense, to define how the class behaves for the benefit of client code using the class, without specifying any implementation. Thus, an interface includes method signatures - the names, return types, and argument lists - for methods expected to be called by client code, and in principle should have plenty of Javadoc for each method describing what that method does. The most compelling reason for using an interface is if you plan to have multiple different implementations of the interface, perhaps selecting an implementation depending on deployment configuration.

A Java abstract class, in contrast, provides a partial implementation of the class, rather than having a primary purpose of specifying an interface. It should be used when multiple classes share code, but when the subclasses are also expected to provide part of the implementation. This permits the shared code to appear in only one place - the abstract class - while making it clear that parts of the implementation are not present in the abstract class and are expected to be provided by subclasses.

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

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

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

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

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

1.1抽象类与接口的区别

1.1.1. Abstract classes versus interfaces in Java 8
1.1.2. Conceptual Difference:

1.2 Java 8中的接口默认方法

1.2.1. What is Default Method?
1.2.2. ForEach method compilation error solved using Default Method
1.2.3. Default Method and Multiple Inheritance Ambiguity Problems
1.2.4. Important points about java interface default methods:

1.3 Java接口静态方法

1.3.1. Java Interface Static Method, code example, static method vs default method
1.3.2. Important points about java interface static method:

1.4 Java功能接口



1.1.1. Java 8中的抽象类与接口

Java 8 interface changes include static methods and default methods in interfaces. Prior to Java 8, we could have only method declarations in the interfaces. But from Java 8, we can have default methods and static methods in the interfaces. After introducing Default Method, it seems that interfaces and abstract classes are same. However, they are still different concept in Java 8. Abstract class can define constructor. They are more structured and can have a state associated with them. While in contrast, default method can be implemented only in the terms of invoking other interface methods, with no reference to a particular implementation's state. Hence, both use for different purposes and choosing between two really depends on the scenario context.

1.1.2. 概念上的差别:

抽象类对接口的骨架(即部分)实现有效,但如果没有匹配的接口就不应该存在。

因此,当抽象类被有效地简化为低可见性的接口骨架实现时,默认方法是否也能消除这种情况呢?毫无疑问:不!实现接口几乎总是需要一些或全部的类构建工具,而默认方法不具备这些工具。如果某些接口没有,这显然是一个特例,不应该让您误入歧途。

1.2 Java 8中的接口默认方法

Java 8引入了“默认方法”或(防御方法)新特性,允许开发人员在不破坏现有接口实现的情况下向接口添加新方法。它提供了灵活性,允许接口定义实现,在具体类无法为该方法提供实现的情况下,接口定义实现将作为默认值使用。

让我们看一个小例子来理解它是如何工作的:

public interface OldInterface {
    public void existingMethod();
 
    default public void newDefaultMethod() {
        System.out.println("New default method"
               + " is added in interface");
    }
}

下面的类将在Java JDK 8中成功编译,

public class OldInterfaceImpl implements OldInterface {
    public void existingMethod() {
     // existing implementation is here…
    }
}

如果你创建一个OldInterfaceImpl实例:

OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod(); 

1.2.1. 默认的方法:

默认方法从来不是最终的,不能同步,也不能 重写Object的方法。他们总是公开的,这很严重 限制了编写简短且可重用方法的能力。

缺省方法可以提供给接口而不影响类的实现,因为它包含实现。如果在用实现定义的接口中添加的每个方法都不影响实现类。实现类可以覆盖接口提供的默认实现。

默认方法允许向现有接口添加新功能 而不会破坏这些接口的旧实现。

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

不重写默认方法,将继承默认方法。 重写默认方法,类似于重写中的其他方法 子类。 将默认方法重新声明为抽象,从而强制子类为 覆盖它。


1.2.2. 使用默认方法解决了所有方法编译错误

对于Java 8, JDK集合已经被扩展,forEach方法被添加到整个集合中(它与lambdas一起工作)。使用常规方法,代码如下所示,

public interface Iterable<T> {
    public void forEach(Consumer<? super T> consumer);
}

由于此结果每个实现类都有编译错误,因此,默认方法添加了必需的实现,以便现有的实现不应被更改。

具有默认方法的可迭代接口如下所示,

public interface Iterable<T> {
    public default void forEach(Consumer
                   <? super T> consumer) {
        for (T t : this) {
            consumer.accept(t);
        }
    }
}

同样的机制已经被用于在不破坏实现类的情况下在JDK接口中添加流。


1.2.3. 默认方法与多重继承歧义问题

由于java类可以实现多个接口,并且每个接口可以定义具有相同方法签名的默认方法,因此,继承的方法之间可能会发生冲突。

考虑下面的例子,

public interface InterfaceA {  
       default void defaultMethod(){  
           System.out.println("Interface A default method");  
    }  
}
 
public interface InterfaceB {
   default void defaultMethod(){
       System.out.println("Interface B default method");
   }
}
 
public class Impl implements InterfaceA, InterfaceB  {
}

上述代码将无法编译,并出现以下错误:

类Impl继承defaultMethod()的不相关默认值 类型为InterfaceA和InterfaceB

为了修复这个类,我们需要提供默认的方法实现:

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
    }
}

此外,如果我们想调用任何一个super Interface提供的默认实现,而不是我们自己的实现,我们可以这样做:

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
        // existing code here..
        InterfaceA.super.defaultMethod();
    }
}

我们可以选择任何默认实现,也可以同时选择两者作为新方法的一部分。

1.2.4. java接口默认方法的要点:

Java interface default methods will help us in extending interfaces without having the fear of breaking implementation classes. Java interface default methods have bridge down the differences between interfaces and abstract classes. Java 8 interface default methods will help us in avoiding utility classes, such as all the Collections class method can be provided in the interfaces itself. Java interface default methods will help us in removing base implementation classes, we can provide default implementation and the implementation classes can chose which one to override. One of the major reason for introducing default methods in interfaces is to enhance the Collections API in Java 8 to support lambda expressions. If any class in the hierarchy has a method with same signature, then default methods become irrelevant. A default method cannot override a method from java.lang.Object. The reasoning is very simple, it’s because Object is the base class for all the java classes. So even if we have Object class methods defined as default methods in interfaces, it will be useless because Object class method will always be used. That’s why to avoid confusion, we can’t have default methods that are overriding Object class methods. Java interface default methods are also referred to as Defender Methods or Virtual extension methods.

资源链接:

何时使用:Java 8+接口默认方法,vs.抽象方法 JDK 8时代的抽象类与接口 通过虚拟扩展方法进行接口演化


1.3 Java接口静态方法

1.3.1. Java接口静态方法,代码示例,静态方法与默认方法

Java接口静态方法与默认方法类似,只是我们不能在实现类中重写它们。这个特性可以帮助我们避免在实现类中实现不好的情况下产生不希望看到的结果。让我们用一个简单的例子来研究这个问题。

public interface MyData {

    default void print(String str) {
        if (!isNull(str))
            System.out.println("MyData Print::" + str);
    }

    static boolean isNull(String str) {
        System.out.println("Interface Null Check");

        return str == null ? true : "".equals(str) ? true : false;
    }
}

现在让我们来看看一个实现类,它有一个isNull()方法,但实现很差。

public class MyDataImpl implements MyData {

    public boolean isNull(String str) {
        System.out.println("Impl Null Check");

        return str == null ? true : false;
    }
    
    public static void main(String args[]){
        MyDataImpl obj = new MyDataImpl();
        obj.print("");
        obj.isNull("abc");
    }
}

注意isNull(String str)是一个简单的类方法,它不会覆盖接口方法。例如,如果我们将@Override注释添加到isNull()方法,它将导致编译器错误。

现在,当我们运行应用程序时,会得到以下输出。

接口空检查 Impl Null检查

如果我们将接口方法从静态改为默认,我们将得到以下输出。

Impl Null检查 MyData打印: Impl Null检查

Java接口静态方法只对接口方法可见,如果我们从MyDataImpl类中删除isNull()方法,我们将不能将它用于MyDataImpl对象。然而,像其他静态方法一样,我们可以使用类名来使用接口静态方法。例如,一个有效的语句将是:

boolean result = MyData.isNull("abc");

1.3.2. java接口静态方法的要点:

Java interface static method is part of interface, we can’t use it for implementation class objects. Java interface static methods are good for providing utility methods, for example null check, collection sorting etc. Java interface static method helps us in providing security by not allowing implementation classes to override them. We can’t define interface static method for Object class methods, we will get compiler error as “This static method cannot hide the instance method from Object”. This is because it’s not allowed in java, since Object is the base class for all the classes and we can’t have one class level static method and another instance method with same signature. We can use java interface static methods to remove utility classes such as Collections and move all of it’s static methods to the corresponding interface, that would be easy to find and use.


1.4 Java功能接口

在我结束这篇文章之前,我想简单介绍一下Functional接口。只有一个抽象方法的接口称为功能接口。

引入了一个新的注释@FunctionalInterface,将接口标记为Functional interface。@FunctionalInterface注释是避免在函数接口中意外添加抽象方法的工具。这是可选的,但使用它是很好的实践。

函数式接口是人们期待已久的Java 8特性,因为它允许我们使用lambda表达式来实例化它们。添加了一个新的包java.util.function,其中包含一系列函数接口,为lambda表达式和方法引用提供目标类型。我们将在以后的文章中讨论函数接口和lambda表达式。

资源位置:

Java 8接口更改-静态方法,默认方法