普通接口 class 和只有抽象方法的抽象 class 之间有什么区别吗?

Are there any differences between a normal interface class and an abstract class that only has abstract methods?

我只是好奇他们的待遇是否有所不同。

例如,如果我们有:

界面:

public interface Test {
    public void method();
}

和摘要class:

public abstract class Test {
    public abstract void method();
}

JVM 对待这些 class 会有什么不同吗?在存储期间,两者中哪一个占用更多磁盘space,哪个将使用最多运行时内存哪个执行更多操作(表现更好)。

这个问题不是关于何时使用接口或抽象classes。

是的,它们是不同的。

有了接口,客户可以实现它,也可以扩展 class:

class ClientType implements YourInterface, SomeOtherInterface { //can still extend other types

}

对于 class,客户将能够扩展它,但不能扩展任何其他类型:

class ClientType extends YourClass { //can no longer extend other types

}

另一个区别出现在 interfaceabstract class 只有一个抽象方法声明时,它与匿名函数 (lambdas) 有关。

正如@AlexanderPetrov 所说,具有一种方法的接口可以用作功能接口,允许我们在功能接口类型所在的地方创建函数"on-the-fly"指定:

//the interface
interface Runnable {
    void run()
}

//where it's specified
void execute(Runnable runnable) {
    runnable.run();
}

//specifying argument using lambda
execute(() -> /* code here */);

abstract class 无法做到这一点。 所以你不能互换使用它们。不同之处在于客户端如何使用它的限制,这是由 JVM 的语义强制执行的。

至于资源使用方面的差异,不用担心除非它导致您的软件问题使用内存管理语言的想法是,除非您遇到问题,否则不要担心这些事情。不要预先优化,我确信差异可以忽略不计。即使存在差异,只有当它可能对您的软件造成问题时才重要。

如果您的软件存在资源问题,请分析您的应用程序。如果它确实导致内存问题,您将能够看到它,以及每个消耗的资源量。在那之前,你不应该担心它。您应该更喜欢使您的代码更易于管理的功能,而不是消耗最少资源的功能。

JVM 内部结构和内存表示 JVM 几乎相同。我的陈述基于第 4 章 - Class 文件格式。从随附的文档可以看出,JVM 通过 access_flags 在 class 和接口之间产生差异。如果您有一个 只有一种方法 的简单接口和一个 只有一种方法 的简单抽象 class。这种格式的大部分字段都是相同的(空的),主要区别在于 access_flags.

默认构造函数生成摘要class 正如@Holger 指出的那样,Interface 和 Abstract class 之间的另一个小区别是普通的 classes 需要一个构造函数。 Java 编译器将为抽象 class 生成一个默认构造函数,每个子class 都会被调用。从这个意义上讲,与接口相比,抽象 class 定义会稍微大一些。

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

除了接口的多重继承外,还有一个区别是Java8中只有一个方法的抽象class不是Functional接口

 @FunctionalInterface
 public interface SimpleFuncInterface {
      public void doWork();
 }

 execute(SimpleFuncInterface function) {
      function.doWork();
 }

 execute(()->System.out.printline("Did work"));

同样不能用抽象实现class。

接口 - 缺少 "Openness to extension"。 多达 Java 8 个接口因缺乏可扩展性而受到批评。如果更改接口契约,则需要重构接口的所有客户端。

One example that comes to mind is Java MapReduce API for Hadoop, which was changed in 0.20.0 release to favour abstract classes over interfaces, since they are easier to evolve. Which means, a new method can be added to abstract class (with default implementation), with out breaking old implementations of the class.

引入Java8个接口默认方法 这种缺乏可扩展性的问题已得到解决。

public interface MyInterface {
 int method1();
 // default method, providing default implementation
 default String displayGreeting(){
  return "Hello from MyInterface";
 }
}

使用 Java 8 个新方法可以添加到接口和抽象 classes 而不会破坏客户端 classes 的合同。 http://netjs.blogspot.bg/2015/05/interface-default-methods-in-java-8.html

如果你谈论性能,那么前段时间有人认为接口比抽象慢 类。但现在 JIT 对它们没有任何影响。

请参阅 this answer 了解更多详情。

实现方式不同

  • 您只能扩展一个摘要class
  • 另一方面,您可以同时实现多个接口
  • 您需要实现接口中存在的所有方法
  • 对于抽象,它可能会提供一些默认实现,因此无论您是想再次实现它还是只使用默认实现,您都是独立的