关于Java:我应该如何解释接口和抽象类之间的区别?

How should I have explained the difference between an Interface and an Abstract class?

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

我的回答是:

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.

然而,面试官并不满意,他告诉我,这种描述代表着"书生气的知识"。

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

我哪里出错了?


我先举个例子:

1
2
3
4
public interface LoginAuth{
   public String encryptPassword(String pass);
   public void checkDBforUser();
}

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

1
2
3
4
5
6
7
8
9
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()不依赖于数据库,并且对每个类都是相同的呢?那么,上述方法就不是一个好方法。

相反,考虑这种方法:

1
2
3
4
5
6
7
8
9
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();
}

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

我尽力了,希望这能消除你的疑虑。


世界上没有完美的东西。他们可能一直在期待一种更实际的方法。

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

  • 接口是规则(规则,因为您必须为它们提供一个不能忽略或避免的实现,以便它们像规则一样被强制执行),它在软件开发的各个团队中起到了共同的理解文档的作用。

  • 接口提供了要做什么,但不提供如何做的概念。因此,通过遵循给定的规则(即给定方法签名),实现完全依赖于开发人员。

  • 抽象类可以包含抽象声明、具体实现,或者两者都包含。

  • 抽象声明类似于要遵循的规则,具体的实现类似于指导原则(您可以按原样使用它,或者您可以通过重写并赋予自己的实现来忽略它)。

  • 此外,将具有相同签名的哪些方法可以更改不同上下文中的行为作为接口声明提供,作为在不同上下文中相应实现的规则。

  • 编辑:Java 8有助于定义接口中的默认方法和静态方法。

    1
    2
    3
    4
    5
    6
    7
    8
    public interface SomeInterfaceOne {

        void usualAbstractMethod(String inputString);

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

    现在,当一个类将实现SomeInterface时,不必为接口的默认方法提供实现。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface SomeInterfaceTwo {

        void usualAbstractMethod(String inputString);

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

    }

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

    1
    SomeInterfaceOne and SomeInterfaceTwo

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


    您对使用和实现上的实际差异做了很好的总结,但没有提到意义上的差异。

    接口是对实现类将具有的行为的描述。实现类确保,它将具有可以在其上使用的这些方法。这基本上是一个合同或一个班级必须作出的承诺。

    抽象类是共享不需要重复创建的行为的不同子类的基础。子类必须完成行为,并且可以选择覆盖预定义的行为(只要它没有定义为finalprivate)。

    您将在java.util包中找到很好的例子,该包包括诸如List之类的接口和已经实现该接口的抽象类,如AbstractList。官方文件对AbstractList的描述如下:

    This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a"random access" data store (such as an array).


    接口由单例变量(公共静态final)和公共抽象方法组成。我们通常喜欢在知道要做什么但不知道如何做的情况下实时使用接口。

    这个概念可以通过示例更好地理解:

    考虑付款类。付款可以在许多方面,如贝宝,信用卡等,所以我们通常把付款作为我们的接口,其中包含一个EDCOX1×1的方法,信用卡和PayPal是两个实现类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public interface Payment
    {
        void makePayment();//by default it is a abstract method
    }
    public class PayPal implements Payment
    {
        public void makePayment()
        {
            //some logic for PayPal payment
            //e.g. Paypal uses username and password for payment
        }
    }
    public class CreditCard implements Payment
    {
        public void makePayment()
        {
            //some logic for CreditCard payment
            //e.g. CreditCard uses card number, date of expiry etc...
        }
    }

    在上面的示例中,信用卡和PayPal是两种实现类/策略。接口还允许我们在爪哇中不能通过抽象类实现多重继承的概念。

    当有一些我们知道要做什么的特性和其他我们知道如何执行的特性时,我们选择一个抽象类。

    请考虑以下示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public abstract class Burger
    {
        public void packing()
        {
            //some logic for packing a burger
        }
        public abstract void price(); //price is different for different categories of burgers
    }
    public class VegBerger extends Burger
    {
        public void price()
        {
            //set price for a veg burger.
        }
    }
    public class NonVegBerger extends Burger
    {
        public void price()
        {
            //set price for a non-veg burger.
        }
    }

    如果我们将来向给定的抽象类添加方法(具体/抽象),那么实现类将不需要更改其代码。但是,如果将来在接口中添加方法,则必须将实现添加到实现该接口的所有类中,否则将发生编译时错误。

    还有其他的差异,但这些是主要的差异,可能是你的面试官所期望的。希望这是有帮助的。


    Abstract类与接口的区别

  • Java 8中抽象类与接口的接口
  • 概念差异:
  • Java 8中的接口缺省方法

  • 什么是默认方法?
  • 使用默认方法解决ForEach方法编译错误
  • 默认方法与多重继承歧义问题
  • Java接口缺省方法要点
  • Java接口静态方法

  • Java接口静态方法,代码示例,静态方法vs默认方法
  • Java接口静态方法要点
  • Java功能接口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.

    Ok.

    After introducing Default Method, it seems that interfaces and
    abstract classes are same. However, they are still different concept
    in Java 8.

    Ok.

    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.

    Ok.

    概念差异:

    抽象类对接口的骨架(即部分)实现有效,但在没有匹配接口的情况下不应存在。好的。

    所以,当抽象类被有效地降低为低可见性时,接口的框架实现,默认方法也能消除这一点吗?坚决地说:不!实现接口几乎总是需要一些或所有默认方法所缺少的类构建工具。如果某个接口没有,这显然是一个特殊的情况,不应该导致你误入歧途。好的。Java 8中的接口缺省方法

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

    让我们考虑一个小例子来理解它是如何工作的:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    public interface OldInterface {
        public void existingMethod();
     
        default public void newDefaultMethod() {
            System.out.println("New default method"
                   +" is added in interface");
        }
    }

    下面的类将在JavaJDK 8中成功编译,好的。

    1
    2
    3
    4
    5
    public class OldInterfaceImpl implements OldInterface {
        public void existingMethod() {
         // existing implementation is here…
        }
    }

    如果创建oldInterfaceImpl的实例:好的。

    1
    2
    3
    OldInterfaceImpl obj = new OldInterfaceImpl ();
    // print"New default method add in interface"
    obj.newDefaultMethod(); 

    默认方法:

    Default methods are never final, can not be synchronized and can not
    override Object’s methods. They are always public, which severely
    limits the ability to write short and reusable methods.

    Ok.

    默认方法可以提供给接口,而不影响实现类,因为它包括实现。如果在用实现定义的接口中添加了每个方法,则不影响实现类。实现类可以重写接口提供的默认实现。好的。

    Default methods enable to add new functionality to existing Interfaces
    without breaking older implementation of these Interfaces.

    Ok.

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

  • 不重写默认方法,将继承默认方法。
  • 重写与我们在中重写的其他方法类似的默认方法子类。
  • 将默认方法重新声明为抽象方法,强制子类重写它。
  • 使用默认方法解决ForEach方法编译错误

    对于Java 8,JDK集合已经被扩展,并且FulACH方法被添加到整个集合中(与LAMBDAS一起工作)。按照常规方式,代码如下所示:好的。

    1
    2
    3
    public interface Iterable<T> {
    &nbsp;&nbsp;&nbsp;&nbsp;public void forEach(Consumer<? super T> consumer);
    }

    由于这会导致每个实现类都有编译错误,因此,为了不改变现有的实现,添加了一个带有必需实现的默认方法。好的。

    默认方法的iterable接口如下,好的。

    1
    2
    3
    4
    5
    6
    7
    8
    public interface Iterable<T> {
    &nbsp;&nbsp;&nbsp;&nbsp;public default void forEach(Consumer
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<? super T> consumer) {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (T t : this) {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;consumer.accept(t);
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
    &nbsp;&nbsp;&nbsp;&nbsp;}
    }

    同样的机制也被用于在JDK接口中添加流,而不破坏实现类。好的。默认方法与多重继承歧义问题

    由于Java类可以实现多个接口,每个接口都可以定义具有相同方法签名的默认方法,因此,继承的方法可以相互冲突。好的。

    考虑下面的例子,好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public interface InterfaceA {&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;default void defaultMethod(){&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Interface A default method");&nbsp;
    &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;
    }
    &nbsp;
    public interface InterfaceB {
    &nbsp;&nbsp;&nbsp;default void defaultMethod(){
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Interface B default method");
    &nbsp;&nbsp;&nbsp;}
    }
    &nbsp;
    public class Impl implements InterfaceA, InterfaceB&nbsp; {
    }

    以上代码编译失败,错误如下:好的。

    java: class Impl inherits unrelated defaults for defaultMethod() from
    types InterfaceA and InterfaceB

    Ok.

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

    1
    2
    3
    4
    public class Impl implements InterfaceA, InterfaceB {
    &nbsp;&nbsp;&nbsp;&nbsp;public void defaultMethod(){
    &nbsp;&nbsp;&nbsp;&nbsp;}
    }

    此外,如果我们想调用任何超级接口提供的默认实现,而不是我们自己的实现,我们可以这样做:好的。

    1
    2
    3
    4
    5
    6
    public class Impl implements InterfaceA, InterfaceB {
    &nbsp;&nbsp;&nbsp;&nbsp;public void defaultMethod(){
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// existing code here..
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InterfaceA.super.defaultMethod();
    &nbsp;&nbsp;&nbsp;&nbsp;}
    }

    我们可以选择任何默认实现,或者两者都作为新方法的一部分。好的。Java接口缺省方法要点

  • Java接口默认方法将帮助我们扩展接口,而不必担心实现类的中断。
  • Java接口默认方法已经缩小了接口和抽象类之间的差异。
  • Java 8接口默认方法将帮助我们避免实用类,例如可以在接口本身中提供所有的集合类方法。
  • Java接口默认方法将帮助我们移除基本实现类,我们可以提供默认实现,而实现类可以选择覆盖哪个实现类。
  • 在接口中引入默认方法的一个主要原因是增强Java 8中的集合API以支持lambda表达式。
  • 如果层次结构中的任何类具有相同签名的方法,则默认方法将变得不相关。默认方法无法重写java.lang.object中的方法。推理非常简单,这是因为对象是所有Java类的基类。因此,即使我们在接口中将对象类方法定义为默认方法,它也将毫无用处,因为对象类方法将始终被使用。这就是为什么为了避免混淆,我们不能有覆盖对象类方法的默认方法。
  • Java接口默认方法也称为防御方法或虚拟扩展方法。
  • 资源链接:

  • Java 8中缺省方法与抽象类的接口
  • JDK8时代的抽象类与接口
  • 基于虚拟扩展方法的接口演化
  • Java接口静态方法

    Java接口静态方法,代码示例,静态方法vs默认方法好的。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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()方法。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 annotation添加到isNull()方法,它将导致编译器错误。好的。

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

    Interface Null Check

    Ok.

    Impl Null Check

    Ok.

    如果我们将接口方法从静态设置为默认设置,我们将得到以下输出。好的。< Buff行情>

    IMPL空校验好的。

    MyDATA打印:好的。

    IMPL空校验好的。< /块引用>

    Java接口静态方法仅对接口方法可见,如果从MyDATAIML类中移除ISNULL()方法,我们将无法将其用于MyDATaIMPL对象。但是,与其他静态方法一样,我们可以使用使用类名的接口静态方法。例如,有效的语句将是:好的。

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

    Java接口静态方法要点

  • Java接口静态方法是接口的一部分,我们不能用它来实现类对象。
  • Java接口静态方法有助于提供实用方法,例如空校验、集合排序等。
  • Java接口静态方法帮助我们通过不允许实现类重写它们来提供安全性。
  • 我们不能为对象类方法定义接口静态方法,我们会得到编译错误,因为"这个静态方法不能从对象隐藏实例方法"。这是因为它不允许在Java中,因为对象是所有类的基类,我们不能有一个类级静态方法和另一个具有相同签名的实例方法。
  • 我们可以使用Java接口静态方法来移除诸如集合之类的实用工具类,并将它的所有静态方法移到相应的接口,这将很容易找到和使用。
  • Java功能接口

    在我结束本文之前,我想简单介绍一下功能接口。只有一个抽象方法的接口称为函数接口。好的。

    引入了新的注释@FunctionalInterface,将接口标记为功能接口。@FunctionalInterface注释是一种避免在功能接口中意外添加抽象方法的功能。它是可选的,但是使用它是很好的实践。好的。

    功能接口是期待已久的Java 8的许多特点,因为它使我们能够使用lambda表达式来实例化它们。添加了一个新的包java.util.function和一系列函数接口,为lambda表达式和方法引用提供目标类型。我们将在以后的文章中研究函数接口和lambda表达式。好的。资源位置:

  • Java 8接口更改——静态方法、默认方法
  • 好啊。


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

    Methods of a Java interface are implicitly abstract and cannot have implementations

    从文档页面:

    An interface is a reference type, similar to a class, that can contain only
    constants, method signatures, default methods, static methods,and nested types

    Method bodies exist only for default methods and static methods.

    默认方法:

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

    Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.

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

  • 更不用说默认方法了,它允许扩展接口继承默认方法。
  • 重新声明默认方法,使其成为abstract
  • 重新定义默认方法,该方法将重写该方法。
  • 静态方法:

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

    这使您更容易在库中组织助手方法;

    文档页面中关于interface具有staticdefault方法的示例代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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));
        }
    }

    使用以下准则选择是使用接口还是抽象类。

    接口:

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

  • 在几个密切相关的类之间共享代码。它建立了一种关系。

  • 在相关类之间共享公共状态(可以在具体类中修改状态)

  • 相关员额:

    接口与抽象类(常规OO)

    实现vs扩展:何时使用?有什么区别?

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

    无关类可以通过接口拥有功能,但是相关类通过扩展基类来改变行为。


    你的解释看起来不错,但你可能看起来像是在读课本?- -

    我更担心的是,你的例子有多可靠?您是否费心将抽象和接口之间的几乎所有差异都包括在内?

    我个人建议这个链接:http://mindprod.com/jgloss/interfacevsabstract.html表

    想要一份详尽的差异清单……

    希望它能在以后的采访中帮助你和所有其他读者。


    许多初级开发人员错误地将接口、抽象类和具体类看作是同一事物的微小变化,而仅仅从技术角度选择其中一个:我需要多重继承吗?我需要一些地方放一些常用的方法吗?除了具体的课程,我还需要其他的东西吗?这是错误的,隐藏在这些问题中的主要问题是:"我"。当您为自己编写代码时,您很少想到其他现在或将来的开发人员正在开发或使用您的代码。好的。

    接口和抽象类,虽然从技术的角度来看很相似,但它们的含义和目的却完全不同。好的。总结

  • 接口定义了一些实现将为您实现的契约。好的。

  • 抽象类提供了实现可以重用的默认行为。好的。

  • 上面这两点是我在面试时要找的,是一个足够简洁的总结。请继续阅读了解更多详细信息。好的。替代性总结

  • 接口用于定义公共API
  • 抽象类用于内部使用,并用于定义SPI
  • 举例来说

    换言之:具体的类以非常具体的方式完成实际的工作。例如,ArrayList使用一个连续的内存区域以紧凑的方式存储对象列表,它提供快速的随机访问、迭代和就地更改,但在插入、删除甚至偶尔添加时都很糟糕;同时,LinkedList使用双链接节点存储对象列表,而不是RS快速迭代、就地更改和插入/删除/添加,但在随机访问时很糟糕。这两种类型的列表针对不同的用例进行了优化,如何使用它们非常重要。当你试图从一个你经常与之交互的列表中挤出性能时,当选择的列表类型取决于你时,你应该仔细选择你正在实例化的列表。好的。

    另一方面,列表的高级用户并不真正关心它是如何实际实现的,他们应该与这些细节隔离开来。让我们假设Java没有公开EDCOX1的2接口,但只有一个具体的EDCOX1,2的类,实际上是EDOCX1,1,现在是什么。所有Java开发人员都会定制他们的代码以适应实现细节:避免随机访问,添加缓存以加速访问,或者仅仅重新实现EDOCX1 0个方面,尽管它与所有实际使用EDCOX1(2)的代码不兼容。那太可怕了…但是现在想象一下,Java主机实际上会意识到一个链表对于大多数实际用例来说是很糟糕的,并决定切换到一个数组列表中,以获得它们唯一的EDCOX1×2类可用。这将影响到世界上每一个Java程序的性能,人们不会对此感到满意。主要的罪魁祸首是实现细节是可用的,开发人员假定这些细节是他们可以依赖的永久合同。这就是为什么隐藏实现细节并只定义一个抽象契约很重要的原因。这就是接口的目的:定义一个方法接受什么类型的输入,以及期望什么类型的输出,而不暴露所有的胆量,这些胆量会诱使程序员调整他们的代码,以适应将来任何更新都可能改变的内部细节。好的。

    抽象类位于接口和具体类之间。它应该帮助实现共享公共或无聊的代码。例如,AbstractCollection提供了基于大小为0的isEmpty的基本实现,contains作为迭代和比较,addAll作为重复add等。这使实现能够集中于区分它们的关键部分:如何实际存储和检索数据。好的。另一种观点:API与SPI

    接口是代码不同部分之间的低内聚性网关。它们允许库存在和发展,而不会在内部发生变化时破坏每个库用户。它被称为应用程序编程接口,而不是应用程序编程类。在较小的规模上,它们还允许多个开发人员通过有良好文档记录的接口分离不同的模块,在大型项目上成功地协作。好的。

    抽象类是实现接口时要使用的高内聚性帮助器,假定实现细节处于某种级别。或者,抽象类用于定义SPI、服务提供者接口。好的。

    API和SPI之间的区别很微妙,但很重要:对于API,重点是谁使用它,而对于SPI,重点是谁实现它。好的。

    向API添加方法很容易,API的所有现有用户仍将进行编译。向SPI添加方法是困难的,因为每个服务提供者(具体实现)都必须实现新方法。如果使用接口来定义SPI,那么只要SPI契约发生更改,提供者就必须发布一个新版本。如果使用抽象类,则可以根据现有的抽象方法来定义新方法,或者将新方法定义为空的throw not implemented exception存根,这样至少可以让较旧版本的服务实现继续编译和运行。好的。关于Java 8和默认方法的注释

    虽然Java 8为接口引入了默认的方法,这使得接口和抽象类之间的界限更加模糊,但这并不是实现可以重用代码,而是为了更容易地改变既用作API又用作SPI的接口(或者错误地用于定义SPIS而不是抽象类)。好的。"书籍知识"

    OP答案中提供的技术细节被认为是"书本知识",因为这通常是学校和大多数关于语言的技术书籍中使用的方法:什么是东西,而不是如何在实践中使用它,特别是在大规模应用中。好的。

    这里有一个类比:假设问题是:好的。

    What is better to rent for prom night, a car or a hotel room?

    Ok.

    技术答案听起来像:好的。

    Well, in a car you can do it sooner, but in a hotel room you can do it more comfortably. On the other hand, the hotel room is in only one place, while in the car you can do it in more places, like, let's say you can go to the vista point for a nice view, or in a drive-in theater, or many other places, or even in more than one place. Also, the hotel room has a shower.

    Ok.

    这都是真的,但完全忽略了两个完全不同的东西,它们可以同时用于不同的目的,"做它"方面不是这两个选项中最重要的一个。答案缺乏视角,表现出一种不成熟的思维方式,同时正确地呈现出真实的"事实"。好的。好啊。


    接口是一个"契约",其中实现契约的类承诺实现方法。当我将游戏从二维升级到三维时,我必须编写一个界面而不是类,我必须创建一个界面来在游戏的二维和三维版本之间共享类。

    1
    2
    3
    4
    5
    6
    package adventure;
    import java.awt.*;
    public interface Playable {
        public void playSound(String s);
        public Image loadPicture(String s);    
    }

    然后,我可以基于环境实现这些方法,同时仍然能够从一个不知道加载的游戏版本的对象调用这些方法。

    public class Adventure extends JFrame implements Playable

    public class Dungeon3D extends SimpleApplication implements Playable

    public class Main extends SimpleApplication implements AnimEventListener,
    ActionListener, Playable

    通常,在游戏世界中,世界可以是一个抽象类,在游戏中执行方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public abstract class World...

        public Playable owner;

        public Playable getOwner() {
            return owner;
        }

        public void setOwner(Playable owner) {
            this.owner = owner;
        }


    用以下方式思考怎么样?

    • 类与抽象类之间的关系属于"is-a"类型
    • 类和接口之间的关系属于"has-a"类型

    所以当你有一个抽象类哺乳动物,一个亚类人,和一个界面驱动时,你可以说

    • 每个人都是哺乳动物
    • 每个人都有一种驾驶(行为)

    我的建议是,书中的知识短语表明他想听到这两者之间的语义差异(就像这里其他人已经建议的那样)。


    抽象类不是纯粹的抽象BCZ,它是具体方法(实现方法)和未实现方法的集合。但是接口是纯粹的抽象BCZ只有未实现的方法,而不是具体的方法。

    为什么是抽象类?

  • 如果用户想要为所有对象编写通用功能。
  • 抽象类是将来在不影响最终用户的情况下添加更多功能的重新实现的最佳选择。
  • 为什么是接口?

  • 如果用户想要在对象上写不同的功能,那么这将是不同的功能。
  • 接口是最好的选择,如果不需要在接口发布后修改需求。

  • 一个界面就像一组公开记录的有某种效果的基因:DNA测试会告诉我是否有这些基因——如果有,我可以公开让它知道我是一个"载体",我的行为或状态的一部分会与它们相一致。(当然,我可能还有许多其他的基因提供了这个范围之外的特性。)

    一个抽象的阶级就像一个单一性别物种的死祖先一样:她不能被带到生命中,但是一个活的(非抽象的)后代继承了她所有的基因。

    (*)为了延伸这个比喻,假设所有物种的成员都生活在同一个年龄。这意味着一个死祖先的所有祖先也必须是死的——同样,一个活祖先的所有后代必须是活的。


    我为工作做面试,你的回答也会让我觉得不好(对不起,我很诚实)。听起来好像你读过差异并修改了答案,但也许你从未在实践中使用过。

    一个好的解释,为什么你会使用每一个可能比有一个准确的差异解释要好得多。雇主最后通牒要求编程人员做一些不知道的事情,而这在面试中很难证明。如果您申请的是基于技术或文档的工作,而不是开发人员的角色,那么您给出的答案会很好。

    祝你以后面试好运。

    另外,我对这个问题的回答更多的是关于面试技巧,而不是你提供的技术资料。或许可以考虑读一下。https://workplace.stackexchange.com/可能是此类活动的最佳场所。


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


    在Java中选择接口以避免多重继承中的钻石问题。

    如果你想让你的客户机实现你所有的方法,你就去找接口。它意味着您抽象地设计整个应用程序。

    如果您已经知道什么是共同点,那么您可以选择抽象类。例如,以抽象类Car为例。在更高的层次上,您可以实现像calculateRPM()这样的通用汽车方法。这是一种常见的方法,您让客户机实现自己的行为,比如calculateMaxSpeed()等。也许你会通过提供一些你在日常工作中遇到的实时例子来解释。


    接口是纯抽象的。我们在接口中没有任何实现代码。

    抽象类包含方法及其实现。

    单击此处观看有关接口和抽象类的教程


    即使我在多次面试中也遇到过同样的问题,相信我,说服面试官也会让你很难过。如果我从上面得到了所有的答案,那么我需要再加一个关键点,使其更具说服力,并尽可能地利用OO。

    如果您没有计划对规则进行任何修改,为了遵循子类,在很长一段时间内,请转到接口,因为您将无法在其中进行修改,如果这样做,则需要转到所有其他子类中的更改,而如果您认为,您希望重用该功能,设置一些规则并使其打开对于修改,转到抽象类。

    这样想,您已经使用了一个可消费的服务,或者您已经向世界提供了一些代码,并且您有机会修改一些东西,假设进行了安全检查。如果我是代码的使用者,并且在更新后的一天早上,我发现Eclipse中的所有读取标记,那么整个应用程序都会关闭。所以为了防止这样的噩梦,在接口上使用抽象

    我认为这可能会在某种程度上说服面试官……提前进行愉快的面试。


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

    当我试图定义一个类型时,我的对象的用户可以可靠地调用的方法列表,然后我创建一个接口。

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

    这似乎也有点太书生气了(尽管我从来没有见过它在我记忆中的任何地方如此)。如果面试官(或OP)真的希望我在这方面有更多的个人经验,我就已经准备好了一个界面的奇闻轶事是必要的,反之亦然。

    还有一件事。Java 8现在允许您将默认代码放入接口,进一步模糊接口和抽象类之间的界限。但是从我所看到的,这个特性被Java核心库的制造者过度使用。添加了这个特性,正确地说,是为了在不创建二进制不兼容的情况下扩展接口。但是,如果您通过定义接口来创建一个全新的类型,那么该接口应该只是一个接口。如果您还想提供公共代码,那么一定要创建一个助手类(抽象的或具体的)。不要从一开始就把你的界面和你想要改变的功能搞得一团糟。


    在抽象类中,可以编写方法的默认实现!但在界面上你不能。基本上,在接口中存在纯虚拟方法,这些方法必须由实现接口的类实现。


    几乎所有的东西都已经被覆盖了。在abstract类的实际实现上再增加一点:

    abstract keyword is also used just prevent a class from being instantiated. If you have a concrete class which you do not want to be instantiated - Make it abstract.


    从我所理解的和我如何接近,

    接口类似于规范/契约,实现接口类的任何类都必须实现抽象类中定义的所有方法(除了默认方法(在Java 8中介绍))。

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

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


    嗯,现在人们都渴望实际的方法,你说得很对,但大多数面试官都是按照他们目前的要求来面试的,他们希望有一个实际的方法。

    完成答案后,您应该跳到示例上:

    文摘:

    例如,我们有一个薪金函数,它有一些对所有员工都通用的参数。然后我们可以有一个抽象类,称为ctc,它有一个由partialy定义的方法体,它将被所有类型的雇员扩展,并根据他们的额外需求重新定义。为了共同的功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public abstract class CTC {

        public int salary(int hra, int da, int extra)
        {
            int total;
            total = hra+da+extra;
            //incentive for specific performing employee
            //total = hra+da+extra+incentive;
            return total;
        }
    }

    class Manger extends CTC
    {
    }


    class CEO extends CTC
    {
    }

    class Developer extends CTC
    {  
    }

    界面

    Java中的接口允许在不扩展该功能的情况下具有IPCCAE功能,并且您必须清楚在应用程序中要引入的功能签名的实现。它会迫使你有明确的定义。对于不同的功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public interface EmployeType {

        public String typeOfEmployee();
    }

    class ContarctOne implements EmployeType
    {

        @Override
        public String typeOfEmployee() {
            return"contract";
        }

    }

    class PermanentOne implements EmployeType
    {

        @Override
        public String typeOfEmployee() {
            return"permanent";
        }

    }

    通过将methgos定义为抽象类,也可以对抽象类进行这种强制活动,现在类tha扩展抽象类remin abstract one,直到它重写该抽象函数。


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

    在抽象类中,还可以提供所有抽象方法,如接口。

    为什么需要抽象类?

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

    为什么需要接口?

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

  • 您将确定您的需求,并与该需求签订合同。
  • 然后招标来建设你的项目
  • 谁曾经建造过这个项目,应该满足你的要求。但构造逻辑不同于一个供应商到另一个供应商。
  • 在这里,我不关心它们是如何构造的逻辑。最终的目标是否满足了我的要求,那只是我的重点。

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


    是的,你的回答在技术上是正确的,但是你错的地方并没有表现出你理解选择一个而不是另一个的优缺点。此外,他们可能担心/害怕他们的代码库将来与升级的兼容性。这种类型的回答可能会有所帮助(除了你所说的之外):

    "Choosing an Abstract Class over an Interface Class depends on what we
    project the future of the code will be.

    Abstract classes allow better forward-compatibility because you can
    continue adding behavior to an Abstract Class well into the future
    without breaking your existing code --> this is not possible with an
    Interface Class.

    On the other hand, Interface Classes are more flexible than Abstract
    Classes. This is because they can implement multiple interfaces. The
    thing is Java does not have multiple inheritances so using abstract
    classes won't let you use any other class hierarchy structure...

    So, in the end a good general rule of thumb is: Prefer using Interface
    Classes when there are no existing/default implementations in your
    codebase. And, use Abstract Classes to preserve compatibility if you
    know you will be updating your class in the future."

    祝你下次面试好运!


    我将尝试使用实际场景来回答这两个问题。

    接口的有效负载为零,也就是说,不需要维护任何状态,因此更适合将契约(能力)与类关联起来。

    例如,假设我有一个执行某些操作的任务类,现在要在单独的线程中执行一个任务,我不需要扩展线程类,更好的选择是让任务实现可运行的接口(即实现其run()方法),然后将该任务类的对象传递给线程实例并调用其start()方法。

    Now you can ask what if Runnable was a abstract class?

    从技术上讲,这是可能的,但从设计上来说,这是一个糟糕的选择,原因是:

    • runnable没有与之关联的状态,也没有"提供"任何run()方法的默认实现
    • 任务必须扩展它,因此它不能扩展任何其他类
    • 任务没有可作为可运行类的专门化提供的内容,它只需要重写run()方法。

    换句话说,任务类需要一种在线程中运行的能力,它通过实现可运行的接口版本来扩展线程类,从而使其成为线程。

    Simply put us interface to define a capability (contract), while use a
    abstract class to define skeleton (common/partial) implementation of
    it.

    免责声明:下面是愚蠢的例子,尽量不要评判-p

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    interface Forgiver {
        void forgive();
    }

    abstract class GodLike implements Forgiver {
        abstract void forget();
        final void forgive() {
            forget();
        }
    }

    现在你被给予了一个选择,要像上帝一样,但你可以选择只做一个宽恕者(也就是说,不要像上帝一样),并且做:

    1
    2
    3
    4
    5
    class HumanLike implements Forgiver {
        void forgive() {
           // forgive but remember    
        }
    }

    或者你可以选择像上帝一样去做:

    1
    2
    3
    4
    5
    class AngelLike extends GodLike {
        void forget() {
           // forget to forgive    
        }
    }

    P.S.与Java 8接口也可以有静态的以及默认的(可重写的实现)方法,因此差异B/W接口和抽象类甚至更加狭窄。


    据我所知,一个由最终变量和没有实现的方法组成的接口由一个类实现,以获得一组相互关联的方法或方法。另一方面,抽象类可以包含非最终变量和带有实现的方法,通常用作指南,或者用作所有相关或类似类继承自的超类。换句话说,抽象类包含所有子类共享的方法/变量。


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

    接口不是Java接口,而是更一般的术语"接口"——对于代码模块来说,基本上是与使用接口的客户端代码订立的契约。

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

    Java接口只应用作上述一般意义上的接口,以定义类如何使用类来为客户端代码受益,而不指定任何实现。因此,接口包括方法签名(名称、返回类型和参数列表),用于客户机代码预期调用的方法,原则上,每个方法都应该有足够的javadoc来描述该方法的功能。使用接口的最令人信服的原因是,如果您计划对接口进行多个不同的实现,那么可以根据部署配置选择一个实现。

    相比之下,Java抽象类提供了类的部分实现,而不是指定接口的主要目的。它应该在多个类共享代码时使用,但是子类也应该提供部分实现。这允许共享代码只出现在一个地方——抽象类——同时明确表示部分实现不存在于抽象类中,并且期望由子类提供。


    你的回答是正确的,但是面试官需要你根据软件工程的角度来区分,而不是根据Java的细节。

    简单的话:

    一个接口就像一个车间的接口,任何显示在上面的东西都应该在车间中,所以接口中的任何方法都必须在具体类中实现。现在,如果一些类共享一些精确的方法,而其他类则有所不同,该怎么办?假设这个界面是关于一个包含两件东西的商店,假设我们有两个商店都包含运动器材,但一个商店有多余的衣服,另一个商店有多余的鞋子。因此,您要做的是为Sport创建一个抽象类,该类实现了Sports方法,并使另一个方法没有实现。这里的抽象类意味着这个商店本身不存在,但它是其他类/商店的基础。这样,您就可以组织代码,避免复制代码、统一代码以及确保其他类的可重用性等错误。