普通接口类和只有抽象方法的抽象类之间是否存在任何差异?

nic*_*oum 6 java inheritance jvm interface abstract

我只是好奇他们是否有任何不同的待遇.

例如,如果我们有:

界面:

public interface Test {
    public void method();
}
Run Code Online (Sandbox Code Playgroud)

而抽象类:

public abstract class Test {
    public abstract void method();
}
Run Code Online (Sandbox Code Playgroud)

JVM会以不同的方式处理这些类吗?这两个中的哪一个在存储期间占用了更多的磁盘空间,哪一个将耗尽最多的运行时内存,哪一个执行更多操作(性能更好).

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

Vin*_*igh 6

是的,他们是不同的.

通过接口,客户端可以实现它以及扩展类:

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

}
Run Code Online (Sandbox Code Playgroud)

使用类,客户端将能够扩展它,但不扩展任何其他类型:

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

}
Run Code Online (Sandbox Code Playgroud)

interface或者abstract class只有一个抽象方法声明时,会出现另一个差异,它与匿名函数(lambdas)有关.

正如@AlexanderPetrov所说,具有一种方法的接口可以用作功能接口,允许我们"在运行中"创建功能,其中指定了功能接口类型:

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

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

//specifying argument using lambda
execute(() -> /* code here */);
Run Code Online (Sandbox Code Playgroud)

这不能用abstract class.所以你不能互换使用它们.不同之处在于客户端如何使用它的局限性,这是由JVM的语义强制执行的.

至于资源使用的差异,除非它导致您的软件问题,否则不必担心.使用内存管理语言的想法是不要担心这些事情,除非你遇到问题.不要预先优化,我确定差异是可以忽略的.即使存在差异,也应该重要的是它是否可能导致您的软件出现问题.

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


Ale*_*rov 6

JVM内部和内存表示JVM 几乎相同.我的陈述基于第4章 - 类文件格式.从附带的文档中可以看出,JVM通过access_flags在类和接口之间做出区分.如果你有一个简单的接口只有一个方法,并用一个简单的抽象类只有一个方法.这种格式的大多数字段都是相同的(空),主要区别在于access_flags.

默认构造函数生成抽象类 正如@Holger所指出的,Interface和Abstract类之间的另一个小区别是普通类需要构造函数.Java编译器将为 Abstract类生成一个默认构造函数,该构造函数将为每个子类调用.从这个意义上讲,抽象类定义与接口相比会略大一些.

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

除了接口的多重继承之外,另一个区别是在Java8中,只有一个方法的抽象类不是Functional接口.

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

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

 execute(()->System.out.printline("Did work"));
Run Code Online (Sandbox Code Playgroud)

使用抽象类无法实现同样的功能.

接口 - 缺乏"扩展的开放性". Java 8界面因其缺乏可扩展性而受到批评.如果更改接口协定,则需要重构接口的所有客户端.

想到的一个例子是用于Hadoop的Java MapReduce API,它在0.20.0版本中被更改为支持接口上的抽象类,因为它们更容易发展.这意味着,可以将新方法添加到抽象类(使用默认实现),而不会破坏类的旧实现.

随着Java 8 Interface Default方法的引入, 这种缺乏可扩展性的问题已得到解决.

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

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