我该如何解释Interface和Abstract类之间的区别?

Thi*_*ker 456 java oop inheritance abstract-class interface

在我的一次访谈中,我被要求解释InterfaceAbstract类之间的区别.

这是我的回答:

Java接口的方法是隐式抽象的,不能有实现.Java抽象类可以具有实现默认行为的实例方法.

在Java接口中声明的变量默认为final.抽象类可能包含非最终变量.

默认情况下,Java接口的成员是公共的.Java抽象类可以具有类似私有,受保护等类通常的类成员.

应使用关键字"implements"实现Java接口; 应使用关键字"extends"扩展Java抽象类.

接口只能扩展另一个Java接口,抽象类可以扩展另一个Java类并实现多个Java接口.

Java类可以实现多个接口,但它只能扩展一个抽象类.

然而,面试官并不满意,并告诉我这个描述代表了" 书本知识 ".

他告诉我一个更实际的回答,解释我何时会使用实际例子在界面上选择一个抽象类.

我哪里做错了?

Vim*_*era 497

我先给你举个例子:

public interface LoginAuth{
   public String encryptPassword(String pass);
   public void checkDBforUser();
}
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)

但是如果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();
}
Run Code Online (Sandbox Code Playgroud)

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

我尽我所能,希望这会清除你的疑虑.

  • 我不确定这真的解释了它的区别......确定这是一个很好的技巧.我认为值得指出的是,Java 8最终承认C++是正确的,并且可以使用多重继承并且可以使用,因此接口现在不仅可以定义函数签名,还可以提供默认实现.因此,使用界面将是优选的. (91认同)
  • @Neutrino尽管Java允许您实现多个接口,每个接口都提供函数的默认实现,但您仍然只能扩展一个类.因此,使用接口可以为想要使用它的人以及其他接口提供更大的灵活性. (4认同)
  • @thecoshman如果我按照答案中的方式处理问题(实现了一个方法的抽象类和另一个抽象方法)或定义了一个具有默认方法实现的接口,会有什么区别?基本上,我想说的是,你写道“使用接口会更好”,我的问题是 - 为什么? (3认同)
  • @HiradNikoo对于迟到的评论感到抱歉,但我偶然发现了这个帖子.您还可以将类继承视为IS-A关系,而接口则表示"具有某种功能". (3认同)
  • 所以,我想可以公平地说,对于接口,定义的实现取决于实际实现接口的类,而抽象类中的东西是扩展类的类的“核心”;即,它不会改变。 (2认同)

Sha*_*ena 199

在这个世界上没有什么是完美的.他们可能期待更多实用的方法.

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

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

  2. 接口提供了想要做什么但不知道如何做的想法.因此,实现完全依赖于开发人员遵循给定的规则(意味着给定方法的签名).

  3. 抽象类可以包含抽象声明,具体实现或两者.

  4. 抽象声明就像要遵循的规则,具体实现就像指南(你可以按原样使用它,或者你可以通过覆盖并给它自己的实现来忽略它).

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

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

public interface SomeInterfaceOne {

    void usualAbstractMethod(String inputString);

    default void defaultMethod(String inputString){
        System.out.println("Inside SomeInterfaceOne defaultMethod::"+inputString);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在当一个类实现SomeInterface时,并不是必须为接口的默认方法提供实现.

如果我们有另外一个接口,有以下方法:

public interface SomeInterfaceTwo {

    void usualAbstractMethod(String inputString);

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

}
Run Code Online (Sandbox Code Playgroud)

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

SomeInterfaceOne and SomeInterfaceTwo
Run Code Online (Sandbox Code Playgroud)

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

  • +1,这真是一个避免混淆的好答案.但我没有看到任何链接,也不知道为什么你引用那些有价值的线条.如果可能,将它们作为点数:). (11认同)

Dan*_*rps 161

您对使用和实施方面的实际差异做了很好的总结,但没有说明意义上的差异.

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

一个抽象类是共享行为,不需要重复创建不同的子类的基础.子类必须完成该行为并具有覆盖预定义行为的选项(只要它未定义为finalprivate).

你会在java.util包中找到很好的例子,包括接口List和类似AbstractList已经实现接口的抽象类.在官方文档描述AbstractList如下:

此类提供List接口的骨干实现,以最大限度地减少实现由"随机访问"数据存储(例如数组)支持的此接口所需的工作量.

  • 这应该是答案.不是详细信息的列表,而是在接口和抽象类之间产生差异的基本概念,不仅在Java中,而且在一般情况下. (15认同)

小智 84

接口由单例变量(public static final)和公共抽象方法组成.当我们知道该做什么但不知道该怎么做时,我们通常更喜欢实时使用界面.

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

考虑付款类.付款可以通过多种方式进行,例如PayPal,信用卡等.因此我们通常将Payment作为包含makePayment()方法的接口,而CreditCard和PayPal是两个实现类.

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...
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,CreditCard和PayPal是两个实现类/策略.接口还允许我们使用Java中的多继承概念,这是抽象类无法实现的.

我们知道要做什么的一些功能以及我们知道如何执行的其他功能时,我们选择一个抽象类.

请考虑以下示例:

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 嗯,这个答案很有道理,当我们在“接口”和“抽象类”之间进行选择时,这个例子很清楚。 (2认同)

Sky*_*ker 44

Abstact类和接口之间的区别

  1. Java 8中的抽象类与接口
  2. 概念差异:

Java 8中的接口缺省方法

  1. 什么是默认方法?
  2. ForEach方法编译错误使用默认方法解决
  3. 默认方法和多重继承歧义问题
  4. 关于java接口默认方法的重点:

Java接口静态方法

  1. Java接口静态方法,代码示例,静态方法与默认方法
  2. 关于java接口静态方法的重点:

Java功能接口



Java 8中的抽象类与接口

Java 8接口更改包括接口中的静态方法和默认方法.在Java 8之前,我们可以在接口中只有方法声明.但是从Java 8开始,我们可以在接口中使用默认方法和静态方法.

在引入Default Method之后,接口和抽象类似乎是相同的.但是,它们在Java 8中仍然是不同的概念.

抽象类可以定义构造函数.它们更结构化,并且可以具有与它们相关联的状态.相反,默认方法只能在调用其他接口方法的条件下实现,而不能引用特定实现的状态.因此,用于不同目的和在两者之间进行选择实际上取决于场景上下文.

概念差异:

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

因此,当抽象类被有效地简化为低可见性,接口的骨架实现时,默认方法也可以将其除去吗?决定:不!实现接口几乎总是需要默认方法缺少的部分或全部类构建工具.如果某些界面没有,那显然是一个特殊情况,不应该让你误入歧途.

Java 8中的接口缺省方法

Java 8引入了" 默认方法 "或(Defender方法)新功能,它允许开发人员在不破坏这些接口的现有实现的情况下向接口添加新方法.它提供了灵活性,允许接口定义实现,在具体类无法为该方法提供实现的情况下将默认使用.

让我们考虑一些小例子来理解它的工作原理:

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

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

public class OldInterfaceImpl implements OldInterface {
    public void existingMethod() {
     // existing implementation is here…
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您创建OldInterfaceImpl的实例:

OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod(); 
Run Code Online (Sandbox Code Playgroud)

默认方法:

默认方法永远不会是最终的,无法同步,也不能覆盖Object的方法.它们总是公开的,严重限制了编写简短和可重用方法的能力.

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

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

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

  1. 不覆盖默认方法,将继承默认方法.
  2. 重写默认方法,类似于我们在子类中重写的其他方法.
  3. 将默认方法重新声明为抽象,强制子类覆盖它.

ForEach方法编译错误使用默认方法解决

对于Java 8,JDK集合已经扩展,并且forEach方法被添加到整个集合(与lambdas一起使用).使用传统方式,代码如下所示,

public interface Iterable<T> {
    public void forEach(Consumer<? super T> consumer);
}
Run Code Online (Sandbox Code Playgroud)

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

使用Default方法的Iterable接口如下,

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

已经使用相同的机制在JDK接口中添加Stream而不破坏实现类.


默认方法和多重继承歧义问题

由于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  {
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将无法编译,出现以下错误,

java:class Impl从InterfaceA和InterfaceB类型继承defaultMethod()的无关默认值

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

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

我们可以选择任何默认实现或两者作为新方法的一部分.

关于java接口默认方法的重点:

  1. Java接口默认方法将帮助我们扩展接口,而不必担心破坏实现类.
  2. Java接口默认方法缩小了接口和抽象类之间的差异.
  3. Java 8接口的默认方法将帮助我们避免使用实用程序类,例如可以在接口本身中提供所有Collections类方法.
  4. Java接口默认方法将帮助我们删除基本实现类,我们可以提供默认实现,实现类可以选择覆盖哪一个.
  5. 在接口中引入默认方法的一个主要原因是增强Java 8中的Collections API以支持lambda表达式.
  6. 如果层次结构中的任何类具有具有相同签名的方法,则默认方法变得无关紧要.默认方法不能覆盖java.lang.Object中的方法.原因很简单,因为Object是所有java类的基类.因此,即使我们将Object类方法定义为接口中的默认方法,它也将是无用的,因为将始终使用Object类方法.这就是为什么要避免混淆,我们不能有重写Object类方法的默认方法.
  7. Java接口默认方法也称为Defender方法或虚拟扩展方法.

资源链接:

  1. 与Java 8中的默认方法和抽象类的接口
  2. JDK 8时代的抽象类与接口
  3. 通过虚拟扩展方法进行接口演化

Java接口静态方法

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在让我们看一个具有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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

接口空检查

Impl Null Check

如果我们将接口方法从static变为default,我们将得到以下输出.

Impl Null Check

MyData打印::

Impl Null Check

Java接口静态方法只对接口方法可见,如果我们从MyDataImpl类中删除isNull()方法,我们将无法将它用于MyDataImpl对象.但是,与其他静态方法一样,我们可以使用类名称的接口静态方法.例如,有效的语句将是:

boolean result = MyData.isNull("abc");
Run Code Online (Sandbox Code Playgroud)

关于java接口静态方法的重点:

  1. Java接口静态方法是接口的一部分,我们不能将它用于实现类对象.
  2. Java接口静态方法适用于提供实用程序方法,例如null检查,集合排序等.
  3. Java接口静态方法通过不允许实现类覆盖它们来帮助我们提供安全性.
  4. 我们不能为Object类方法定义接口静态方法,我们会得到编译错误为"这个静态方法不能隐藏来自Object的实例方法".这是因为它在java中是不允许的,因为Object是所有类的基类,我们不能有一个类级静态方法和另一个具有相同签名的实例方法.
  5. 我们可以使用java接口静态方法来删除诸如Collections之类的实用程序类,并将其所有静态方法移动到相应的接口,这很容易找到和使用.

Java功能接口

在结束发布之前,我想简要介绍一下Functional接口.只有一个抽象方法的接口称为功能接口.

@FunctionalInterface引入了一个新的注释来将接口标记为功能接口.@FunctionalInterface注释是一种避免在功能界面中意外添加抽象方法的工具.使用它是可选的但很好的做法.

功能接口是期待已久的,并且备受追捧Java 8的特性,因为它使我们能够使用lambda表达式来实例化它们.添加了一个带有大量功能接口的新包java.util.function,以便为lambda表达式和方法引用提供目标类型.我们将在以后的帖子中研究功能接口和lambda表达式.

资源位置:

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

  • 我正在寻找这些类型的更新答案.感谢您的快速回复. (7认同)
  • 非常彻底,但就采访而言,我认为事情变得更糟了!可怜的面试官只是想知道这个人是否可以将他的知识应用到实际情况中,而不是关于该主题的百科全书条目! (2认同)

Rav*_*abu 40

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

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

从文档页面:

接口是一种类似于类的引用类型,它只能包含 常量,方法签名,默认方法,静态方法和嵌套类型

方法体仅适用于默认方法和静态方法.

默认方法:

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

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

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

  1. 根本没有提到默认方法,它允许扩展接口继承默认方法.
  2. 重新声明默认方法,这就是它abstract.
  3. 重新定义默认方法,该方法将覆盖它.

静态方法:

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

这使您可以更轻松地在库中组织辅助方法;

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

接口:

  1. 定义合同(最好是无状态 - 我的意思是没有变量)
  2. 链接不相关的类具有功能.
  3. 声明公共常量变量(不可变状态)

抽象类:

  1. 在几个密切相关的类之间共享代码.它建立的是一种关系.

  2. 相关类之间共享共同状态(状态可以在具体类中修改)

相关文章:

接口与抽象类(通用OO)

Implements vs extends:何时使用?有什么不同?

通过这些例子,您可以理解这一点

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

  • 这是最好的答案。它不仅解决了 Java8,而且还解释了您将在哪些特定情况下使用其中任何一种。 (2认同)

Vic*_*tor 22

你的解释看起来不错,但可能看起来你正在从教科书中读到这一切?: - /

我更烦恼的是,你的榜样有多坚固?您是否懒得包含抽象和接口之间几乎所有的差异?

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

一份详尽的差异清单..

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


Ser*_*riu 21

许多初级开发人员错误地认为接口,抽象和具体类是同一事物的微小变化,并且纯粹基于技术理由选择其中一个:我是否需要多重继承?我需要一些地方来放置常用方法吗?我是否需要打扰除了具体课程之外的其他事情?这是错误的,隐藏在这些问题中的主要问题是:"我".当您自己编写代码时,您很少会想到其他现有或未来的开发人员在使用代码或使用代码.

接口和抽象类虽然从技术角度来看显然相似,但具有完全不同的含义和目的.

摘要

  1. 接口定义了一些实现将为实现的合同.

  2. 抽象类提供了一个默认的行为您的实现可以重复使用.

以上这两点是我在面试时所寻找的,并且是一个足够紧凑的摘要.请阅读以获得更多详情.

替代摘要

  1. 接口用于定义公共API
  2. 抽象类供内部使用,用于定义SPI

以身作则

换句话说:具体的课程以非常具体的方式完成实际工作.例如,ArrayList使用连续的存储区域以紧凑的方式存储对象列表,其提供快速随机访问,迭代和就地更改,但是在插入,删除和偶尔添加时是可怕的; 同时,a LinkedList使用双链节点来存储对象列表,而是提供快速迭代,就地更改和插入/删除/添加,但随机访问非常糟糕.这两种类型的列表针对不同的用例进行了优化,对于您将如何使用它们非常重要.当你试图从你正在与之大量交互的列表中挤出性能时,当你选择列表类型时,你应该仔细挑选你正在实例化的列表.

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

接口和具体类之间是一个抽象类.它应该有助于实现共享共同或无聊的代码.例如,AbstractCollection提供isEmpty基于大小为0的基本实现,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的接口(或错误地用于定义SPI而不是抽象类).

"书本知识"

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

这是一个类比:假设问题是:

为舞会之夜,汽车或酒店房间租用什么更好?

技术答案听起来像:

嗯,在汽车里你可以早点做到,但在酒店房间你可以做得更舒服.另一方面,酒店房间只在一个地方,而在车里,你可以在更多的地方做,比如说,你可以去远景观景点,或在免下车剧院,或许多其他地方,甚至不止一个地方.此外,酒店房间还有淋浴.

这一切都是正确的,但完全错过了两个完全不同的东西,两者可以同时用于不同的目的,而"做它"方面对于两个选项中的任何一个都不是最重要的.答案缺乏视角,它表明了一种不成熟的思维方式,同时正确地呈现了真实的"事实".


Nik*_*ntz 9

接口是一个"契约",实现合同的类承诺实现这些方法.我必须编写一个界面而不是一个类的例子是我将游戏从2D升级到3D时的情况.我必须创建一个界面来在2D和3D版本的游戏之间共享类.

package adventure;
import java.awt.*;
public interface Playable {
    public void playSound(String s);
    public Image loadPicture(String s);    
}
Run Code Online (Sandbox Code Playgroud)

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

public class Adventure extends JFrame implements Playable

public class Dungeon3D extends SimpleApplication implements Playable

public class Main extends SimpleApplication implements AnimEventListener, ActionListener, Playable

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

public abstract class World...

    public Playable owner;

    public Playable getOwner() {
        return owner;
    }

    public void setOwner(Playable owner) {
        this.owner = owner;
    }
Run Code Online (Sandbox Code Playgroud)


ako*_*out 9

怎么想以下方式:

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

因此,当你有一个抽象类Mammals,一个子类Human和一个接口Driving时,你可以说

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

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


小智 6

抽象类不是纯粹的抽象bcz它的具体(实现方法)集合以及未实现的方法.但是接口是纯粹的抽象bcz只有未实现的方法而不是具体的方法.

为什么抽象类?

  1. 如果用户想要为所有对象编写通用功能.
  2. 抽象类是将来重新实现的最佳选择,可以在不影响最终用户的情况下添加更多功能.

为什么接口?

  1. 如果用户想要编写与对象不同的功能的不同功能.
  2. 接口是最佳选择,如果不需要在发布接口后修改要求.


小智 5

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


Ste*_*ers 5

一个界面就像是一组基因被公开记录有一些样的影响:一个DNA测试会告诉我,我是否已经得到了他们-如果我这样做,我可以公开让他们知道,我是一个"载体"我的一部分行为或状态将符合他们.(但是,当然,我可能有许多其他基因提供超出此范围的特征.)

一个抽象类是像的死祖先单性物种(*):她不能被赋予生命,但活(即非抽象)的后裔继承了她所有的基因.

(*)为了扩展这个比喻,让我们说这个物种的所有成员都生活在同一个年龄段.这意味着死去的祖先的所有祖先也必须死亡 - 同样,活着的祖先的所有后代都必须活着.


归档时间:

查看次数:

424949 次

最近记录:

5 年,9 月 前