Java中的抽象类与接口

Java抽象类与接口详解 什么是抽象类,什么是接口,其用法,通过本文,尽收眼底。 阅读详情

在Java中什么时候应该选择抽象类而不是接口?接受挑战吧!了解这些Java语言元素之间的区别以及如何在你的程序中使用它们。

在Java代码中,甚至在Java开发工具包(JDK)本身中,都有大量的抽象类和接口。每个代码元素都有一个基本的目的:

  • 接口 是一种 代码契约 ,必须由一个具体的类来实现。
  • 抽象类 与普通类相似,不同的是它们可以包括 抽象方法 ,也就是没有主体的方法。抽象类不能被实例化。

许多开发者认为接口和抽象类是相似的,但它们实际上是完全不同的。让我们来探讨一下它们之间的主要区别。

接口的本质

从本质上讲,接口是一个契约,所以它依赖于一个实现来达到其目的。一个接口 永远不可能有状态 ,所以它不能使用可变的实例变量。一个接口只能使用最终变量。

何时使用接口

接口对于解耦代码和实现多态性非常有用。我们可以在JDK中看到一个例子,就是 List 接口:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public interface List<E> extends Collection<E> {

    int size();
    boolean isEmpty();
    boolean add(E e);
    E remove(int index);
    void clear();
}
</pre>

正如你可能注意到的,这段代码很短,而且描述性很强。我们可以很容易地看到 方法的签名 ,我们将用一个具体的类来实现接口中的方法。

List 接口包含一个契约,可以由 ArrayList , Vector , LinkedList , 和其他类来实现。

为了使用多态性,我们可以简单地用 List 来声明我们的变量类型,然后选择任何一个可用的实例化。这里有一个例子:

<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">List list = new ArrayList();
System.out.println(list.getClass());

 List list = new LinkedList();
 System.out.println(list.getClass());
</pre>

下面是这段代码的输出:

<pre class="prettyprint hljs ruby" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class java.util.ArrayList
class java.util.LinkedList
</pre>

在这种情况下, ArrayList , LinkedList , 和 Vector 的实现方法都是不同的,这就是使用接口的一个很好的场景。如果你注意到许多类都属于一个父类,其方法动作相同,但行为不同,那么使用接口是个好主意。

接下来,让我们来看看我们可以用接口做的几件事。

重写一个接口方法

记住,接口是一种必须由具体类来实现的契约。接口方法是隐含的抽象的,也需要一个具体类的实现。

这里有一个例子:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class OverridingDemo {
  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }
}

interface Challenger {
  void doChallenge();
}

class JavaChallenger implements Challenger {
  @Override
  public void doChallenge() {
    System.out.println("Challenge done!");
  }
}
</pre>

下面是这段代码的输出:

<pre class="hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Challenge done!
</pre>

注意这个细节,接口方法是 隐式抽象 的。这意味着我们不需要明确地将它们声明为抽象的。

常量变量

另一条要记住的规则是,一个接口只能包含常量变量。因此,下面的代码是可以的:

<pre class="prettyprint hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Challenger {

  int number = 7;
  String name = "Java Challenger";

}
</pre>

注意,这两个变量都是隐含的 final 和 static 。这意味着它们是常量,不依赖于一个实例,而且不能被改变。

如果我们试图改变 Challenger 接口中的变量,例如,像这样:

<pre class="prettyprint hljs lua" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Challenger.number = 8;
Challenger.name = "Another Challenger";
</pre>

我们会触发一个编译错误,像这样:

<pre class="prettyprint hljs xquery" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Cannot assign a value to final variable 'number'
Cannot assign a value to final variable 'name'
</pre>

缺省方法

当 [默认方法在Java 8中被引入 时,一些开发者认为它们会和抽象类一样。然而这并不正确,因为接口不能有状态。

默认方法可以有一个实现,而抽象方法则不能。默认方法是lambdas和流的伟大创新的结果,但我们应该谨慎使用它们。

JDK中使用默认方法的一个方法是 forEach() ,它是 Iterable 接口的一部分。我们可以简单地重用 forEach 方法,而不是将代码复制到每个 Iterable 的实现中:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">default void forEach(Consumer<? super T> action) { 
  // Code implementation here…
</pre>

任何 Iterable 实现都可以使用 forEach() 方法,而不需要新的方法实现。然后,我们可以用一个默认方法来重用代码。

让我们来创建我们自己的默认方法:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class DefaultMethodExample {

  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }

}

class JavaChallenger implements Challenger { }

interface Challenger {

  default void doChallenge() {
    System.out.println("Challenger doing a challenge!");
  }
}
</pre>

下面是输出结果:

<pre class="hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Challenger doing a challenge!
</pre>

关于默认方法,需要注意的是,每个默认方法都需要一个实现。默认方法不能是静态的。

现在,让我们继续讨论抽象类。

抽象类的本质

抽象类可以有实例变量的状态。这意味着一个实例变量可以被使用和变异。这里有一个例子:

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public abstract class AbstractClassMutation {

  private String name = "challenger";

  public static void main(String[] args) {
    AbstractClassMutation abstractClassMutation = new AbstractClassImpl();
    abstractClassMutation.name = "mutated challenger";
    System.out.println(abstractClassMutation.name);
  }

}

class AbstractClassImpl extends AbstractClassMutation { }
</pre>

下面是输出结果:

<pre class="hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">mutated challenger
</pre>

抽象类中的抽象方法

就像接口一样,抽象类可以有抽象方法。 抽象方法 是一个没有主体的方法。与接口不同,抽象类中的抽象方法必须明确地声明为抽象的。这里有一个例子:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public abstract class AbstractMethods {

  abstract void doSomething();

}
</pre>

试图声明一个没有实现的方法,而且没有 abstract 关键字,像这样:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public abstract class AbstractMethods {
   void doSomethingElse();
}
</pre>

导致了一个编译错误,像这样:

<pre class="prettyprint hljs php" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Missing method body, or declare abstract
</pre>

什么时候使用抽象类

当你需要实现可改变状态时,使用抽象类是一个好主意。作为一个例子,Java集合框架包括AbstractList类,它使用变量的状态。

在你不需要维护类的状态的情况下,通常使用一个接口更好。

实践中的抽象类

设计模式中的模板方法是使用抽象类的好例子。模板方法模式在具体方法中操作实例变量。

抽象类和接口的区别

从面向对象编程的角度来看,接口和抽象类的主要区别是,接口 不能 有状态,而抽象类可以用实例变量来有状态。

另一个关键区别是,类可以实现一个以上的接口,但它们只能扩展一个抽象类。这是一个基于多重继承(扩展一个以上的类)会导致代码死锁的设计决定。Java的工程师们决定要避免这种情况。

另一个区别是,接口可以被类实现,也可以被接口扩展,但类只能被扩展。

还需要注意的是,lambda表达式只能用于功能接口(指只有一个方法的接口),而只有一个抽象方法的抽象类 不能 使用lambdas。

接受Java代码挑战吧!

让我们通过一个Java代码挑战来探索接口和抽象类的主要区别。我们在下面提供了代码挑战,你也可以 [用视频的形式观看抽象类与接口的挑战

在下面的代码中,同时声明了一个接口和一个抽象类,而且代码中还使用了lambdas:

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class AbstractResidentEvilInterfaceChallenge {
  static int nemesisRaids = 0;
  public static void main(String[] args) {
    Zombie zombie = () -> System.out.println("Graw!!! " + nemesisRaids++);
    System.out.println("Nemesis raids: " + nemesisRaids);
    Nemesis nemesis = new Nemesis() { public void shoot() { shoots = 23; }};

    Zombie.zombie.shoot();
    zombie.shoot();
    nemesis.shoot();
    System.out.println("Nemesis shoots: " + nemesis.shoots +
        " and raids: " + nemesisRaids);
  }
}
interface Zombie {
  Zombie zombie = () -> System.out.println("Stars!!!");
  void shoot();
}
abstract class Nemesis implements Zombie {
   public int shoots = 5;
}
</pre>

你认为当我们运行这段代码时,会发生什么?请从下列选项中选择一个。

选项A

<pre class="hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Compilation error at line 4
</pre>

选项B

<pre class="prettyprint hljs erlang-repl" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Graw!!! 0
     Nemesis raids: 23
     Stars!!!
     Nemesis shoots: 23 and raids:1
</pre>

选项C

<pre class="prettyprint hljs erlang-repl" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Nemesis raids: 0
     Stars!!!
     Graw!!! 0
     Nemesis shoots: 23 and raids: 1
</pre>

选项D

<pre class="prettyprint hljs erlang-repl" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Nemesis raids: 0
     Stars!!!
     Graw!!! 1
     Nemesis shoots: 23 and raids:1
</pre>

选项E

<pre class="hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Compilation error at line 6
</pre>

了解接口和抽象类及方法

这个Java代码挑战展示了许多关于接口、抽象方法等的重要概念。逐行浏览代码会让我们了解到输出中发生的很多事情。

代码挑战的第一行包括 Zombie 接口的lambda表达式。请注意,在这个lambda中,我们正在增加一个静态字段。实例字段在这里也可以使用,但在lambda之外声明的局部变量就不行了。因此,到目前为止,这段代码可以正常编译。还要注意的是,lambda表达式还没有执行,所以 nemesisRaids 字段还不会被递增。

在这一点上,我们将打印 nemesisRaids 字段,它没有被增加,因为λ表达式还没有被调用,只是被声明。因此,这一行的输出将是:

<pre class="hljs nginx" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Nemesis raids: 0
</pre>

这个Java代码挑战中另一个有趣的概念是,我们正在使用一个 匿名的内层类 。这基本上意味着任何将实现 Nemesis 抽象类的方法的类。我们并没有真正实例化 Nemesis 抽象类,因为它实际上是一个匿名的类。还要注意的是,第一个具体的类在扩展它们的时候总是有义务实现抽象的方法。

在 Zombie 接口里面,我们用一个lambda表达式声明了 zombie static Zombie 接口。因此,当我们调用 zombie shoot 方法时,我们会打印以下内容:

<pre class="hljs erlang-repl" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Stars!!!
</pre>

下一行代码调用了我们在开始时创建的lambda表达式。因此, nemesisRaids 这个变量将被递增。然而,由于我们使用的是后增量运算符,它将只在这条代码语句之后被增量。接下来的输出将是:

<pre class="hljs erlang-repl" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Graw!!! 0 
</pre>

现在,我们将从 nemesis 中调用 shoot 方法,这将改变其 shoots 实例变量为 23 。 注意,这部分代码展示了接口和抽象类之间的最大区别。

最后,我们打印 nemesis.shoots 和 nemesisRaids 的值。因此,输出结果将是:

<pre class="prettyprint hljs vim" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Nemesis shoots: 23 and raids: 1
</pre>

综上所述,正确的输出是选项C:

<pre class="prettyprint hljs erlang-repl" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Nemesis raids: 0
     Stars!!!
     Graw!!! 0
     Nemesis shoots: 23 and raids: 1
</pre>
目标检测YOLO实战应用案例100讲-基于YOLOv7的番茄采摘机械手场景感知及试验(下) 根据软件系统的设计需求,制定系统的可行性方案分析,编写系统的相关程序和 调试运行环境,对系统进行测试,以实现对番茄数据的采集及监测。登陆界面登陆后,进入选择界面,菜单包括了对番茄种植棚室的固定相机数据的监测、 对番茄采摘机械手的双目相机采集的番茄的静态、动态数据的获取、对番茄静态、动态数 据处理的功能菜单等选项。与利用人工去进行对于番茄果实的识别和数目的统计相比,番茄视觉系统自动记录番 茄数量以及红色已经成熟的番茄、青色未成熟番茄的数量,减轻人工记录的工作量,减少 人工记录的误差,从经济方面是可行的。 阅读详情

相关推荐

【已验证】基于STM32F407与AD9833的幅频特性测试装置设计与实现

本文详细介绍了基于STM32F407微控制器和AD9833 DDS芯片的低成本幅频特性测试装置设计与实现方法。通过扫频原理,该装置能自动生成正弦波信号,测量滤波器等电路的输出幅度变化,并在LCD屏幕或示波器上绘制幅频特性曲线。文章提供了从硬件选型、CubeMX工程配置、关键驱动代码编写到数据处理与可视化的完整教程,帮助电子爱好者与工程师快速搭建实用的滤波器测试工具。

weixin_35649491的博客 121

Java中的接口以及抽象类与接口的关系

例如,`Duck`具有飞行和游泳的能力,所以`Duck`实现`Flyable`和`Swimmable`接口。- 在这个例子中,`Mammal`接口继承了`Animal`接口,这意味着实现`Mammal`接口的类不仅要实现`giveBirth`方法,还要实现`eat`方法。例如,`Circle`是一种`Shape`,所以`Circle`可以继承`Shape`抽象类。- 在这个`Shape`抽象类中,`color`是成员变量,`getColor`是普通的非抽象方法,`getArea`是抽象方法。

2301_77081232的博客 1326

MT6816磁编码器在无人机电调中的实战应用:从选型到调试全记录

本文详细介绍了MT6816磁编码器在无人机电调系统中的实战应用,涵盖选型、硬件集成、软件配置到飞行测试的全流程。通过对比霍尔传感器和光电编码器,突出MT6816在角度分辨率、安装公差和抗振动能力等方面的优势,为无人机动力系统提供高精度控制解决方案。

pandas7gardener的博客 234

【Java 基础】抽象类与接口详解

本文详细介绍了Java中抽象类和接口的核心概念与应用。抽象类通过abstract关键字定义,用于封装子类共性,强制子类实现抽象方法,不能实例化但可包含构造器。接口突破单继承限制,定义行为规范,JDK8后支持默认/静态方法。二者关键区别在于:抽象类侧重代码复用,接口侧重行为规范。文章通过动物类、USB设备等案例演示了实际应用,并解答了常见面试问题,帮助开发者深入理解面向对象设计中这两个重要特性的本质与适用场景。

小旭9527的博客 589

内网CentOS离线部署LibreOffice:从依赖缺失到服务启动的完整避坑指南

本文详细介绍了在CentOS内网环境中离线部署LibreOffice的完整流程,包括依赖包准备、安装顺序优化及常见报错解决方案。针对金融等封闭网络场景,提供从rpm包选择到服务启动的全套避坑指南,特别解决libXinerama等图形库缺失问题,确保文档转换服务稳定运行。

weixin_33416697的博客 246

AD(AltiumDesigner)封装导入allegro生成allegro封装

AD(AltiumDesigner)封装导入allegro生成allegro封装 貌似PCB的封装问题是一个无尽话题,为了简单我总是想找捷径(主要还是太懒了),因为也是近期才开始学习使用allegro进行PCB设计,在最近的一个设计中,很多器件封装allegro中都没有,一个一个画感觉很费时间,但是在AD(AltiumDesigner)的封装库中是有的,于是便想着AD的封装库能不能倒入到alleg...

yangh2560的博客 2万+

Java 抽象与接口

1、什么是抽象类与抽象方法用关键字abstract修饰的类称为抽象类。用关键字abstract修饰的方法称为抽象方法。2、抽象类的特点抽象类可以包含抽象方法也可以不包含抽象方法。抽象类不能直接实例化。抽象类必须有子类继承并且子类必须覆写所有的抽象方法。抽象类不能使用final修饰,抽象方法也不能使用final修饰。3、抽象类的作用可以使程序的设计者把主要精力放在程序的设计上,而不必拘泥于细节的实现上//构造方法省略//抽象方法。

wxh的博客 773

C++11、C++14 和 C++17 标准新特性详解

特性C++11C++14C++17auto类型推导✅范围 for 循环✅Lambda 表达式✅✅✅智能指针✅移动语义✅nullptr✅统一初始化{}✅✅constexpr✅✅✅✅数字分隔符✅结构化绑定✅✅✅文件系统库✅。

h a 6 6 6 c k 为 你解决复杂难题! 442

JAVA中的“抽象接口”

       在程序设计过程中,读者很可能遇到这样一种困境:设计了一个接口,但实现这个接口的子类并不需要实现接口中的全部方法,也就是说,接口中的方法过多,对于某些子类是多余的,我们不得不浪费的写上一个空的实现。        今天小菜提到的“抽象接口”,就是用来解决这个问题的。        为了不误导读者,先说明一下,什么是“抽象接口”。        所谓“抽象接口”,即在提供接口的同时...

weixin_33913332的博客 154

零基础学习CANoe Panel(17)—— Panel CAPL Function

CANoe内置了几个CAPLFunction,允许我们在CAPL运行时动态的更改Panel控件的属性,比如打开某个panel,禁用某个控件,更改控件颜色等。

蚂蚁小兵 3520

【花雕学编程】Arduino ESP32 SPP 之使用Servo库控制360度舵机正反转

使用 Servo 库控制 360 度舵机的正反转是 Arduino ESP32 SPP 应用中的一个重要案例,具有简易控制接口、灵活速度调节和高兼容性等特点,广泛应用于机器人、自动化装置和动态艺术等领域。在第三个案例中,使用map()函数将用户输入的速度参数(范围0-100)映射到舵机控制的角度(0-180),使得用户能够以更人性化的方式控制舵机速度。5、创新:Arduino可以让你用电子的方式来表达你的创意和想象,你可以用Arduino来制作各种有趣和有用的项目,如机器人、智能家居、艺术装置等。

雕爷学编程 1469

Java抽象类和接口

本篇文章我们只谈了两件事:抽象类-接口。抽象类和接口是Java中两个非常重要的概念,它们各有独特的优势,共同特点是都很抽象。

LEE180501的博客 4656

YOLOv8改进,YOLOv8主干网络替换为FasterNet(全网独发手把手教学,助力涨点)

FasterNet的整体架构由四个分层阶段组成,每个阶段包含一组FasterNet模块,并在前面加一个嵌入或合并层。最后三层用于特征分类。每个FasterNet模块内部,一个PConv层后跟两个PWConv层,为了保持特征多样性并降低延迟,归一化和激活层仅在中间层之后进行,其中,PConv 是一种快速高效的卷积操作,通过仅对部分输入通道应用卷积滤波器,而保持其余通道不变,从而减少了计算量和内存访问。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 2018

Java 抽象类和接口

文章目录一、抽象类1.抽象类1.1抽象类的定义1.2抽象方法的定义方式1.3抽象类的定义方式2.抽象类和实例类的区别3.抽象类示例4.抽象类的特征二、接口1.接口1.1接口的定义1.1定义一个接口1.2 接口的使用1.3接口的注意事项2.接口示例3.接口的特征三、抽象类和接口的区别 一、抽象类 1.抽象类 1.1抽象类的定义 在Java面向对象当中,所有的对象都是用过类进行描绘的,但是并不是所有的类都是用来描绘对象的,如果一个类没有包含足够多的信息来描述一个具体的对象,这样的类就是抽象类。 1.2抽象方法的

WYSCODER的博客 4869

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【Java SE】抽象类和接口

本期主要讲解: 1、抽象类 1.1 抽象类的概念 1.2 抽象类的语法 1.3 抽象类的特性 1.4 抽象类的作用 2、接口 2.1 接口的概念 2.2 接口的语法规则 2.3 接口的简单使用 2.4 接口的特性 2.5 实现多个接口 2.6 接口之间的继承 2.7 抽象类和接口的区别 3、Object 类 3.1 认识Object类 3.2 获取对象信息 3.3 对象的比较 equals 方法​​​​​​​......

程序猿教你打篮球的博客 1874

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

java之抽象类与接口

带你轻松了解抽象类与接口

G13138200303的博客 2001

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

java中抽象类和接口

当然有抽象类也有抽象方法,

有待提高 1159

Java中的抽象类和接口

这个就有点点好玩的了,我们前面说过如果一个子类继承了我们的抽象方法就必须在我们的子类里面去重写父类的抽象方法,当有意思的就来了 当我们的子类B也是抽象方法的时候就不用与重写父类中的抽象方法了, 但是但是有句话说的好 “出来混迟早是要还的”-----当我们在拿一个子类去继承C前面那个子类B的话,那么这个子类C既要重写父类的抽象方法也要与重写子类B的抽象方法。这个时候我们就要去认识一个新的语法----抽象类了,抽象类里面的语法是不需要在自己里面实现的,而当子类继承抽象类的话一定要去重写抽象类里面的方法。

weixin_74352792的博客 2038

Java接口和抽象类

Java中接口是很重要的

二哈喇子! 2665

Java中的接口及抽象类

两篇文章以不同的方式陈述了这个问题:文章1,转自:http://hi.baidu.com/klcstudy/blog/item/65e5333f129c0bec55e72313.htmlJava接口特性学习    在Java中看到接口,第一个想到的可能就是C++中的多重继承和Java中的另外一个关键字abstract。从另外一个角度实现多重继承是接口的功能之一,接口的存在可以使Ja

Day day up! 1万+

Java中抽象类与接口详解

private我们已经知道,抽象类就是为了被继承,但是private修饰的并不支持继承,自然抽象类中也不能有private修饰了finalfinal修饰的类不支持继承,所以也不能与抽象类中abstract在一起使用static抽象方法不能用static修饰,static修饰的方法是类级别的方法,不是对象级别的方法.而抽象类使用来被继承的.首先抽象类是半抽象的,接口是完全抽象的,这也决定了他们的不同1.一个类可以实现(implements)多个接口,但是一个普通类只能继承(extends)一个抽象类。

qm_hu的博客 1497

Java的抽象类和接口

Java的抽象类和接口相关知识~

2301_80313139的博客 751
上一篇: 怎样让Java编译器帮你写代码
下一篇: Maven 入门具体教程
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值