521

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

这是我的回应:

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

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

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

Java 接口应该使用关键字“implements”来实现;Java 抽象类应该使用关键字“extends”进行扩展。

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

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

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

他要求我给出更实际的回应,并使用实际示例解释我何时会选择抽象类而不是接口

我哪里做错了?

4

31 回答 31

554

我先给你举个例子:

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

假设您的应用程序中有 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
}

但是,如果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();
}

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

于 2013-09-13T04:26:55.533 回答
218
于 2013-09-13T04:15:21.887 回答
189

您很好地总结了使用和实现的实际差异,但没有说明含义上的差异。

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

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

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

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

于 2013-09-13T04:16:24.717 回答
93

接口由单例变量(公共静态最终)和公共抽象方法组成。当我们知道该做什么但不知道如何做时,我们通常更喜欢实时使用界面。

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

考虑一个支付类。可以通过多种方式进行支付,例如 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...
    }
}

在上面的示例中,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.
    }
}

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

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

于 2014-02-25T20:39:41.017 回答
61

1.1 Difference between Abstract class and interface

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

1.2 Interface Default Methods in 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 Interface Static Method

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 Functional Interfaces



1.1.1. Abstract classes versus interfaces in 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. Conceptual Difference:

Abstract classes are valid for skeletal (i.e. partial) implementations of interfaces but should not exist without a matching interface.

So when abstract classes are effectively reduced to be low-visibility, skeletal implementations of interfaces, can default methods take this away as well? Decidedly: No! Implementing interfaces almost always requires some or all of those class-building tools which default methods lack. And if some interface doesn’t, it is clearly a special case, which should not lead you astray.

1.2 Interface Default Methods in Java 8

Java 8 introduces “<a href="http://zeroturnaround.com/rebellabs/java-8-explained-default-methods/" rel="noreferrer">Default Method” or (Defender methods) new feature, which allows developer to add new methods to the Interfaces without breaking the existing implementation of these Interface. It provides flexibility to allow Interface define implementation which will use as default in the situation where a concrete Class fails to provide an implementation for that method.

Let consider small example to understand how it works:

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

The following Class will compile successfully in Java JDK 8,

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

If you create an instance of OldInterfaceImpl:

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

1.2.1. Default Method:

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.

Default methods can be provided to an Interface without affecting implementing Classes as it includes an implementation. If each added method in an Interface defined with implementation then no implementing Class is affected. An implementing Class can override the default implementation provided by the Interface.

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

When we extend an interface that contains a default method, we can perform following,

  1. Not override the default method and will inherit the default method.
  2. Override the default method similar to other methods we override in subclass.
  3. Redeclare default method as abstract, which force subclass to override it.

1.2.2. ForEach method compilation error solved using Default Method

For Java 8, the JDK collections have been extended and forEach method is added to the entire collection (which work in conjunction with lambdas). With conventional way, the code looks like below,

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

Since this result each implementing Class with compile errors therefore, a default method added with a required implementation in order that the existing implementation should not be changed.

The Iterable Interface with the Default method is below,

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

The same mechanism has been used to add Stream in JDK Interface without breaking the implementing Classes.


1.2.3. Default Method and Multiple Inheritance Ambiguity Problems

Since java Class can implement multiple Interfaces and each Interface can define default method with same method signature, therefore, the inherited methods can conflict with each other.

Consider below example,

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  {
}

The above code will fail to compile with the following error,

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

In order to fix this class, we need to provide default method implementation:

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

Further, if we want to invoke default implementation provided by any of super Interface rather than our own implementation, we can do so as follows,

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

We can choose any default implementation or both as part of our new method.

1.2.4. Important points about java interface default methods:

  1. Java interface default methods will help us in extending interfaces without having the fear of breaking implementation classes.
  2. Java interface default methods have bridge down the differences between interfaces and abstract classes.
  3. 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.
  4. 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.
  5. One of the major reason for introducing default methods in interfaces is to enhance the Collections API in Java 8 to support lambda expressions.
  6. 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.
  7. Java interface default methods are also referred to as Defender Methods or Virtual extension methods.

Resource Link:

  1. When to use: Java 8+ interface default method, vs. abstract method
  2. Abstract class versus interface in the JDK 8 era
  3. Interface evolution via virtual extension methods

1.3 Java Interface Static Method

1.3.1. Java Interface Static Method, code example, static method vs default method

Java interface static method is similar to default method except that we can’t override them in the implementation classes. This feature helps us in avoiding undesired results incase of poor implementation in implementation classes. Let’s look into this with a simple example.

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;
    }
}

Now let’s see an implementation class that is having isNull() method with poor implementation.

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");
    }
}

Note that isNull(String str) is a simple class method, it’s not overriding the interface method. For example, if we will add @Override annotation to the isNull() method, it will result in compiler error.

Now when we will run the application, we get following output.

Interface Null Check

Impl Null Check

If we make the interface method from static to default, we will get following output.

Impl Null Check

MyData Print::

Impl Null Check

Java interface static method is visible to interface methods only, if we remove the isNull() method from the MyDataImpl class, we won’t be able to use it for the MyDataImpl object. However like other static methods, we can use interface static methods using class name. For example, a valid statement will be:

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

1.3.2. Important points about java interface static method:

  1. Java interface static method is part of interface, we can’t use it for implementation class objects.
  2. Java interface static methods are good for providing utility methods, for example null check, collection sorting etc.
  3. Java interface static method helps us in providing security by not allowing implementation classes to override them.
  4. 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.
  5. 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 Interfaces

Before I conclude the post, I would like to provide a brief introduction to Functional interfaces. An interface with exactly one abstract method is known as Functional Interface.

A new annotation @FunctionalInterface has been introduced to mark an interface as Functional Interface. @FunctionalInterface annotation is a facility to avoid accidental addition of abstract methods in the functional interfaces. It’s optional but good practice to use it.

Functional interfaces are long awaited and much sought out feature of Java 8 because it enables us to use lambda expressions to instantiate them. A new package java.util.function with bunch of functional interfaces are added to provide target types for lambda expressions and method references. We will look into functional interfaces and lambda expressions in the future posts.

Resource Location:

  1. Java 8 Interface Changes – static method, default method
于 2016-06-13T03:43:55.507 回答
43

除了您的第一条语句(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));
    }
}

使用以下指南来选择是使用接口还是抽象类。

界面:

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

抽象类:

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

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

相关文章:

接口 vs 抽象类(通用 OO)

实现与扩展:何时使用?有什么不同?

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

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

于 2016-01-24T16:55:06.220 回答
24

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

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

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

对于差异的详尽列表..

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

于 2013-09-13T04:07:24.550 回答
23

许多初级开发人员错误地将接口、抽象类和具体类视为同一事物的细微变化,并纯粹出于技术原因选择其中之一:我需要多重继承吗?我需要一些地方来放置常用方法吗?除了具体的课程之外,我还需要为其他事情烦恼吗?这是错误的,隐藏在这些问题中的是主要问题:“我”。当您自己编写代码时,您很少会想到其他现在或未来的开发人员正在处理或使用您的代码。

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

概括

  1. 接口定义了一些实现将为完成的契约。

  2. 抽象类提供您的实现可以重用的默认行为。

上面这两点是我面试的时候要找的,是一个足够紧凑的总结。请阅读以获得更多详情。

替代摘要

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

举例

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

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

抽象类位于接口和具体类之间。它应该帮助实现共享通用或无聊的代码。例如,AbstractCollection提供isEmpty基于 size 为 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 答案中提供的技术细节被认为是“书籍知识”,因为这通常是学校和大多数关于语言的技术书籍中使用的方法:事物是什么,而不是如何在实践中使用它,尤其是在大规模应用程序中.

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

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

技术答案听起来像:

好吧,在汽车上你可以更快地做到这一点,但在酒店房间里你可以更舒适地做到这一点。另一方面,酒店房间只有一个地方,而在车上你可以在更多地方做,比如说你可以去远景点看风景,或者在汽车影院,或许多其他地方,甚至不止一个地方。另外,酒店房间有淋浴。

这都是真的,但完全忽略了它们是两个完全不同的东西,两者都可以同时用于不同目的的观点,而“做它”方面并不是这两个选项中最重要的事情. 答案缺乏视角,显示了一种不成熟的思维方式,同时正确地呈现了真实的“事实”。

于 2016-06-19T08:37:47.310 回答
10

接口是一个“契约”,实现契约的类承诺实现方法。当我将游戏从 2D 升级到 3D 时,我不得不编写接口而不是类的一个例子。我必须创建一个接口来在游戏的 2D 和 3D 版本之间共享类。

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

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

public abstract class World...

    public Playable owner;

    public Playable getOwner() {
        return owner;
    }

    public void setOwner(Playable owner) {
        this.owner = owner;
    }
于 2013-09-13T04:08:48.260 回答
9

如何思考以下方式:

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

所以当你有一个抽象类Mammals,一个子类Human,和一个接口Driving,那么你可以说

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

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

于 2014-11-13T09:55:46.047 回答
6

抽象类不是纯粹的抽象,因为它包含具体(实现的方法)以及未实现的方法。但是接口是纯粹的抽象,因为只有未实现的方法而不是具体的方法。

为什么选择抽象类?

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

为什么是接口?

  1. 如果用户想要编写不同的功能,那将是对象上的不同功能。
  2. 如果接口发布后不需要修改需求,接口是最佳选择。
于 2014-01-05T04:16:49.277 回答
5

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

于 2013-10-28T04:56:04.423 回答
5

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

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

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

于 2016-06-17T08:50:04.233 回答
4

我为工作进行面试,我也会对你的回答不满意(对不起,但我很诚实)。听起来您确实已经阅读了差异并修改了答案,但也许您从未在实践中使用过它。

一个很好的解释为什么你会使用每个比对差异有一个精确的解释要好得多。雇主最终希望程序员做他们不了解的事情,这在面试中很难证明。如果申请基于技术或文档的工作而不是开发人员角色,您给出的答案会很好。

祝以后面试顺利。

我对这个问题的回答更多的是关于面试技巧,而不是你提供的技术材料。也许考虑阅读它。https://workplace.stackexchange.com/可能是处理这类事情的好地方。

于 2013-09-13T04:16:34.230 回答
4

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

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

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

当然,从 Java 8 开始,事情发生了一些细微的变化,但想法还是一样的。

如果你没有接受编译器团队的面试,我想这对于典型的 Java 面试来说已经足够了。

于 2019-11-26T10:55:02.440 回答
3

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

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

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

于 2013-10-08T09:38:11.430 回答
3

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

如果您希望您的所有方法都由您的客户端实现,您可以选择接口。这意味着您以抽象的方式设计整个应用程序。

如果您已经知道共同点,则选择抽象类。例如拿一个抽象类Car。在更高级别上,您可以实现常见的汽车方法,例如calculateRPM(). 这是一种常见的方法,您让客户实现他自己的行为,例如
calculateMaxSpeed()等。您可能会通过提供一些您在日常工作中遇到的实时示例来进行解释。

于 2013-09-13T04:35:28.133 回答
3

即使我在多次面试中都遇到过同样的问题,相信我,说服面试官会让你的时间很痛苦。如果我继承了上面的所有答案,那么我需要再添加一个关键点,以使其更具说服力并充分利用 OO

如果您不打算对规则进行任何修改,对于要遵循的子类,在很长一段时间内,请选择接口,因为您将无法在其中进行修改,如果您这样做,您需要选择所有其他子类都发生了变化,而如果您认为要重用该功能,设置一些规则并使其开放以供修改,请选择抽象类。

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

我认为这可能会在一定程度上说服面试官……Happy Interviews Ahead。

于 2015-03-06T18:06:57.690 回答
3

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

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

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

这可能看起来也有点太书呆子了(尽管我从未见过它在我记得的任何地方都是这样的)。如果面试官(或 OP)真的想要更多我的个人经验,我会准备好接口已经进化出必要性的轶事,反之亦然。

还有一件事。 Java 8 现在允许您将默认代码放入接口中,进一步模糊了接口和抽象类之间的界限。但据我所知,即使是 Java 核心库的制造商,该功能也被过度使用。添加了该功能,并且正确地添加了该功能,以便可以在不造成二进制不兼容的情况下扩展接口。但是如果你通过定义一个接口来创建一个全新的类型,那么接口应该只是一个接口。如果您还想提供通用代码,那么一定要创建一个辅助类(抽象或具体)。不要从一开始就将您的界面与您可能想要更改的功能混为一谈。

于 2016-06-17T04:15:29.327 回答
2

hmm now the people are hungery practical approach, you are quite right but most of interviewer looks as per their current requirment and want a practical approach.

after finishing your answer you should jump on the example:

Abstract:

for example we have salary function which have some parametar common to all employee. then we can have a abstract class called CTC with partialy defined method body and it will got extends by all type of employee and get redeined as per their extra beefits. For common functonality.

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
{   
}

Interface

interface in java allow to have interfcae functionality without extending that one and you have to be clear with the implementation of signature of functionality that you want to introduce in your application. it will force you to have definiton. For different functionality.

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";
    }

}

you can have such forced activity with abstract class too by defined methgos as a abstract one, now a class tha extends abstract class remin abstract one untill it override that abstract function.

于 2014-01-09T06:44:24.173 回答
2

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

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

为什么需要抽象类?

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

为什么需要接口?

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

  1. 您将确定您的要求并根据该要求签订合同。
  2. 然后调用标书来构建您的项目
  3. 谁曾经建造过这个项目,那应该满足您的要求。但是构建逻辑从一个供应商到另一个供应商是不同的。

在这里,我不关心他们如何构建的逻辑。最终的对象是否满足我的要求,那只是我的重点。

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

于 2018-01-19T07:10:39.667 回答
2

为了将其简化为您可以在面试中提供的简单合理的回答,我提供以下内容...

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

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

如果你想确定它,那么就说抽象类通常实现接口的一部分——工作是你的!

于 2021-02-17T17:56:02.420 回答
1

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

于 2013-10-14T12:41:56.747 回答
1

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

于 2013-10-28T05:00:56.470 回答
1

这里似乎已经涵盖了几乎所有内容。在类的实际实现上再添加一点abstract

abstract关键字也用于防止类被实例化。如果您有一个不想被实例化的具体类 - 制作它abstract

于 2013-11-19T18:26:45.040 回答
1

这是一个以 Java 8 为中心的解释,它试图展示抽象类和接口之间的主要区别,并涵盖了Java Associate Exam所需的所有细节。

关键概念:

  • 一个类extend只能有一个类,但可以implement有任意数量的接口
  • 接口定义类做什么,抽象类定义它是什么
  • 抽象类是类。它们不能被实例化,但在其他方面表现得像普通类
  • 两者都可以有抽象方法静态方法
  • 接口可以有默认方法静态最终常量,并且可以扩展其他接口
  • 所有接口成员都是公共的(直到 Java 9)

接口定义类做什么,抽象类定义它是什么

罗迪格林

接口通常用于描述一个类的能力,而不是它的中心标识,例如,一个汽车类可能实现 Recyclable 接口,它可以应用于许多不相关的对象。抽象类定义了其后代的核心身份。如果你定义了一个 Dog 抽象类,那么 Dalmatian 的后代就是 Dogs,它们不仅仅是可狗的。

在 Java 8 之前,@Daniel Lerps 的回答很明确,接口就像实现类必须履行的合同。

现在,使用默认方法,它们更像是一个Mixin,它仍然强制执行合同,但也可以提供代码来完成工作。这允许接口接管抽象类的一些用例。

抽象类的关键在于它缺少抽象方法形式的功能。如果一个类没有任何抽象行为(在不同类型之间变化),那么它可能是一个具体的类。

抽象类是类

下面是一些在抽象类中可用但在接口中不可用的类的正常特性:

  • 实例变量/非最终变量。因此……</li>
  • 可以访问和修改对象状态的方法
  • 私有/受保护成员(但请参阅 Java 9 的注释)
  • 扩展抽象或具体类的能力
  • 构造函数

关于抽象类的注意事项:

  • 它们不能final(因为它们的全部目的是要扩展)
  • 扩展另一个抽象类的抽象类继承其所有抽象方法作为它自己的抽象方法

抽象方法

抽象类和接口都可以有零到多个抽象方法。抽象方法:

  • 是没有主体的方法签名(即 no {}
  • 在抽象类中必须用abstract关键字标记。在接口中这个关键字是不必要的
  • 不能private(因为它们需要由另一个类实现)
  • 不能final(因为他们还没有身体)
  • 不能static因为原因

另请注意:

  • 抽象方法可以被同一个类/接口中的非抽象方法调用
  • 第一个扩展抽象类或实现接口的具体类必须为所有抽象方法提供实现

静态方法

可以直接调用抽象类上的静态方法MyAbstractClass.method();(即与普通类一样,也可以通过扩展抽象类的类调用)。

接口也可以有静态方法。这些只能通过接口名称 ( MyInterface.method();) 调用。这些方法:

  • 不能abstract,即必须有身体(参见上面的“因为原因”)
  • 不是default(见下文)

默认方法

接口可以具有必须具有default关键字和方法体的默认方法。这些只能引用其他接口方法(并且不能引用特定实现的状态)。这些方法:

  • 不是static
  • 不是abstract(他们有身体)
  • 不能final(名称“default”表示它们可能被覆盖)

如果一个类使用具有相同签名的默认方法实现两个接口,则会导致编译错误,可以通过覆盖该方法来解决该错误。

接口可以有静态最终常量

接口只能包含上述类型的方法或常量。

常量被假定为staticand final,并且可以在没有限定的情况下在实现接口的类中使用。

所有接口成员都是公开的

在 Java 8 中,所有接口成员(以及接口本身)都假定为public,并且不能为protectedor private(但 Java 9确实允许接口中的私有方法)。

这意味着实现接口的类必须定义具有公共可见性的方法(符合不能以较低可见性覆盖方法的正常规则)。

于 2020-06-18T19:50:56.130 回答
1

我将尝试使用实际场景来回答,以显示两者之间的区别。

接口的负载为零,即无需维护状态,因此将合同(能力)与类关联是更好的选择。

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

现在你可以问如果 Runnable 是一个抽象类怎么办?

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

  • Runnable 没有与之关联的状态,也没有为 run() 方法“提供”任何默认实现
  • 任务必须扩展它,因此它不能扩展任何其他类
  • Task 没有什么可以提供作为 Runnable 类的特化,它所需要的只是覆盖 run() 方法

换句话说,Task 类需要一种在线程中运行的能力,它通过实现 Runnable 接口来实现,而不是扩展 Thread 类使其成为线程。

简单地说,我们用接口来定义一个能力(契约),同时使用一个抽象类来定义它的骨架(通用/部分)实现。

免责声明:愚蠢的例子如下,尽量不要判断:-P

interface Forgiver {
    void forgive();
}

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

现在,您可以选择成为 GodLike,但您可以选择仅成为 Forgiver(即不是 GodLike)并执行以下操作:

class HumanLike implements Forgiver {
    void forgive() {
       // forgive but remember    
    }
}

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

class AngelLike extends GodLike {
    void forget() {
       // forget to forgive     
    }
}

具有 java 8 接口的PS也可以具有静态以及默认(可覆盖实现)方法,因此黑白接口和抽象类的差异更加缩小。

于 2017-05-04T19:47:35.667 回答
1

根据我的理解和我的处理方式,

接口就像一个规范/契约,任何实现接口类的类都必须实现抽象类中定义的所有方法(默认方法除外(Java 8 中引入))

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

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

于 2016-06-17T09:13:06.933 回答
1

是的,您的回答在技术上是正确的,但是您出错的地方并没有向他们表明您了解选择一个而不是另一个的好处和坏处。此外,他们可能担心/害怕他们的代码库与未来升级的兼容性。这种类型的回应可能有帮助(除了你所说的):

“选择抽象类而不是接口类取决于我们对代码未来的预测。

抽象类允许更好的前向兼容性,因为您可以在未来继续向抽象类添加行为而不会破坏现有代码-->这对于接口类是不可能的。

另一方面,接口类比抽象类更灵活。这是因为它们可以实现多个接口。问题是Java没有多重继承,所以使用抽象类不会让你使用任何其他类层次结构......

所以,最后一个好的一般经验法则是:当你的代码库中没有现有/默认实现时,更喜欢使用接口类。而且,如果您知道将来会更新您的类,请使用抽象类来保持兼容性。”

祝你下次面试好运!

于 2016-06-19T15:53:18.147 回答
0

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

简单的话:

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

于 2016-11-27T18:06:07.693 回答
0

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

代码模块的接口——不是 Java 接口,而是更一般的“接口”——基本上是与使用该接口的客户端代码签订的合同。

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

Java 接口仅应用作上述通用意义上的接口,以定义类如何为使用该类的客户端代码的利益而行为,而无需指定任何实现。因此,接口包括方法签名——名称、返回类型和参数列表——用于期望由客户端代码调用的方法,并且原则上每个方法都应该有大量的 Javadoc 来描述该方法的作用。使用接口最令人信服的原因是,如果您计划有多个不同的接口实现,可能会根据部署配置选择一个实现。

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

于 2016-06-20T01:52:22.673 回答