第一,抽象类:
我们都知道,在面向对象的领域中,一切都是一个对象,所有的对象都是用类来描述的,但不是所有的类都是用对象来描述的。如果一个类没有足够的信息来描述一个特定的对象,并且需要其他特定的类来支持它,那么我们称它为抽象类。例如,新动物(),我们都知道这是为了生产一种动物-动物-动物的物体,但我们不知道动物是什么样子的。它没有特定动物的概念,因此它是一个抽象类。我们需要一种特殊的动物,比如狗和猫,以一种特定的方式来描述它,这样我们就能知道它是什么样子的。
在面向对象的领域中,抽象概念在问题域中没有对应的具体概念,因此无法实例化表示抽象概念的抽象类。
同时,抽象类体现了数据抽象的思想,是实现多态性的一种机制。它定义了一组抽象方法,并使用派生类来表示它们。同时,抽象类提供了继承的概念,其出发点是继承,否则就不存在任何意义上的继承。因此,定义的抽象类必须用于继承。同时,在以抽象类为节点的继承关系层次链中,叶节点必须是具体的实现类。
使用抽象类时需要注意以下几点:
无法实例化抽象类。实例化的工作应该留给它的子类。它只需要一个参考。
抽象方法必须由子类重写。
只要它包含一个抽象方法的抽象类,该方法就必须被定义为一个抽象类,不管它是否包含其他方法。
抽象类可以包含具体方法,但也不能包含抽象方法。
抽象不能与final并排修改同一个类。
抽象不能同时使用private、static、final或native修改同一方法。
例子:
定义一个抽象的动物类动物,并提供一个名为cry()的抽象方法。猫和狗都是动物类的子类。因为cry()是一个抽象方法,所以cat和dog必须实现cry()方法。如下:
public abstract class Animal {
public abstract void cry();
}
public class Cat extends Animal{
@Override
public void cry() {
System.out.println("猫叫:喵喵...");
}
}
public class Dog extends Animal{
@Override
public void cry() {
System.out.println("狗叫:汪汪...");
}
}
public class Test {
public static void main(String[] args) {
Animal a1 = new Cat();
Animal a2 = new Dog();
a1.cry();
a2.cry();
}
}
创建抽象类和方法非常有用,因为它们可以澄清类的抽象性,并告诉用户和编译器如何使用它们。抽象类是有用的重构器,因为它们允许我们轻松地将通用方法向继承层次结构上移动。
二、接口
接口比抽象类更抽象。我在这里找不到一个更好的单词来引用“class”,但是让我们澄清一下,接口本身不是一个类,正如我们不能实例化接口的事实所看到的那样。例如,new runnable();必须是错误的,我们只能新建它的实现类。
接口用于在类之间建立协议,这些类只提供没有特定实现的表单。同时,实现接口的实现类必须实现接口的所有方法。通过使用implements关键字,他指示类正在跟踪特定接口或一组特定接口。同时,他还指出“接口只是它的外观,但现在我们需要声明它是如何工作的”。
接口是抽象类的扩展。Java保证没有多重继承的数据安全性。也就是说,继承只能存在于一个父类中。但是不同的接口,一个类可以同时实现多个接口。不管这些接口之间是否存在任何关系,接口弥补了抽象类不能继承多个接口的不足,但建议继承和接口一起使用,因为这两者都是。它可以保证数据安全,实现多重继承。
在使用界面的过程中,应注意以下问题:
接口的所有方法访问权限都自动声明为公共。确切地说,它只能是公开的。当然,您可以将声明显示为受保护的和私有的,但是编译会出错!
成员变量或不可变常量可以在接口中定义,因为它们自动成为公共静态final。通过类命名直接访问:实现类。姓名。
接口中没有实现方法。
实现接口的非抽象类必须实现接口的所有方法。抽象类可以在不需要的情况下实现。
不能使用new运算符实例化接口,但可以声明一个接口变量,该变量必须引用实现接口的类的对象。可以使用InstanceOf检查对象是否实现特定接口。例如:if(AnObject Instance of Comparable)。
在实现多个接口时,必须避免方法名的重复。
三、抽象类与接口的区别
尽管抽象类和接口有很多共同点,有时它们可以互换,但这并不能弥补它们之间的差异。接下来,抽象类和接口将从两个方面进行阐述:语法级别和设计级别。
3.1语法水平
在语法层面上,Java语言给出抽象类和接口的不同定义。下面的演示类说明了它们之间的区别。
使用抽象类实现:
public abstract class Demo { abstract void method1(); void method2(){ //实现 } }
使用接口实现
interface Demo { void method1(); void method2(); }
在抽象类方法中,抽象类可以有任意范围的成员数据,也可以有自己的非抽象方法。但在接口方法中,它只能有静态和不可修改的成员数据(但我们通常不在接口中使用成员数据)。同时,它的所有方法都必须是抽象的。在某种程度上,接口是抽象类的专门化。
对于子类,它只能继承一个抽象类(它被Java考虑用于数据安全),但是它可以实现多个接口。
3.2设计水平
以上只是从语法层次和编程角度来区分它们之间的关系,这些都是低级的,要真正使用抽象类和接口,我们必须从更高层次来区分。只有从设计理念的角度才能看到它们的本质。一般来说,它们有以下三个区别:
抽象的层次是不同的。抽象类是类的抽象,接口是行为的抽象。抽象类是抽象整个类,包括属性、行为,而接口是抽象类的局部(行为)。
跨领域差异。抽象类跨具有相似特性的域,而接口可以跨具有不同类的域。我们知道抽象类从子类中发现公共部分,然后归纳为抽象类。子类可以继承父类,但接口不同。实现它的子类之间不能有任何共同点。例如,猫和狗可以用吠叫方法抽象成一个抽象的动物类。鸟类和飞机可以实现飞行界面,具有飞行行为。我们不能在这里和父母一人分享鸟类和飞机。所以抽象类体现了一种继承关系。为了使继承关系合理化,父类和派生类之间必须有一个“is-a”关系,即父类和派生类的概念在本质上应该是相同的。然而,对于接口,它并不要求接口的实现者和接口定义的实现者在概念上基本相同,而是只实现接口定义的契约。
设计水平不同。对于抽象类,它是自下而上设计的。在抽象父类之前,我们需要知道子类,但是接口是不同的。它根本不需要知道子类的存在。它只需要定义一个规则,我们不知道什么子类,何时以及如何实现它们。例如,我们这里只有一只猫。如果你把它抽象成动物,它是不是有点设计过度了?我们这里至少应该有两个动物班,猫和狗。让我们抽象它们的共同点,形成动物抽象类。所以抽象类经常被重构!但是界面是不同的。例如,飞行,我们不知道什么将实现飞行接口。如何实现它还不得而知。我们需要做的是预先定义好飞行的行为界面。所以抽象类是从下到上抽象的,接口是从上到下设计的。
为了更好地说明它们之间的差异,将使用一个示例来说明以下内容。这个例子引用自:blog。CSDN网/ttgjz/arti…
我们有一个门的抽象概念,它有两个行为,打开()和关闭(),我们可以通过抽象类和接口来定义抽象概念:
抽象类:
abstract class Door{ abstract void open(); abstract void close(); }
界面
interface Door{ void open(); void close(); }
对于其他具体类,可以使用扩展类和抽象类来定义门,也可以使用接口实现来定义门。结果表明,二者之间无显著性差异。
但是现在如果我们需要一个具有报警功能的门,那么如何实现它呢?
解决方案1:向门添加报警方法:clarm();
abstract class Door{ abstract void open(); abstract void close(); abstract void alarm(); }
也许
interface Door{ void open(); void close(); void alarm(); }
这种方法违反了面向对象设计的核心原则之一ISP(接口分离原则)。请注意,在门的定义中,它将门概念的固有行为方法与另一个概念“报警”的行为方法混合在一起。出现的一个问题是,仅依赖门概念的模块将随着“警报”概念的变化而变化,反之亦然。
解决方案2
由于open()、close()和alarm()属于两个不同的概念,我们根据isp原理将它们分别定义为表示两个不同概念的两个抽象类。定义它们有三种方法:
1。两者都由抽象类定义。
2。两者都由接口定义。
3、一个使用抽象类定义,一个是用接口定义。
因为Java不支持多重继承,所以第一个是不可行的。后两种方法是可行的,但是选择反映了您对问题域本质的理解。
如果第二个问题是由接口定义的,那么它反映了两个问题:1。我们可能不清楚问题域,而报警门本质上是一个门到门的报警。2。例如,如果我们对问题域的理解没有问题,我们已经确定在我们的分析中,报警门的概念本质上是相同的,那么我们在设计中没有正确地反映我们的设计意图。因为您使用两个接口来定义,所以它们的概念的定义并不反映上述含义。
第三,如果我们理解问题域如下:报警门本质上是一个门,但它也具有报警的行为功能,那么我们可以使用第三种方案来说明我们的设计意图。报警门本质上是人,所以我们使用抽象类来定义这个概念。同时,报警门具有报警功能,表明它能够完成报警概念中定义的行为功能,因此报警可以使用接口来定义报警门。如下:
abstract class Door{ abstract void open(); abstract void close(); } interface Alarm{ void alarm(); } class AlarmDoor extends Door implements Alarm{ void open(){} void close(){} void alarm(){} }
这种实现方式基本上可以清楚地反映我们对问题领域的理解,并正确地揭示我们的设计意图。实际上,抽象类表示“is-a”关系,接口表示“like-a”关系。每个人都可以选择它作为基础。当然,这是基于对问题域的理解。例如,如果我们认为报警门在概念上本质上是一个报警,并且具有门的功能,那么上述定义将被颠倒。
注释:
isp(接口分离原则):面向对象的核心原则。结果表明,使用多个专用接口优于使用单个主接口。
一个类与另一个类的依赖关系应该基于最小的接口。
接口代表一个角色,不应将不同的角色分配给一个接口。不相关的接口合并在一起形成一个膨胀的大接口,这是角色和接口的污染。
四、总结
1。抽象类表示Java语言中的继承关系。子类只能有一个父类,但可以有多个接口。
2。在抽象类中,我们可以有自己的成员变量和非抽象类方法,但是在接口中只能有静态和不可变的成员数据(尽管我们通常不在接口中定义成员数据),并且它的所有方法都是抽象的。
三。抽象类和接口反映了不同的设计概念。抽象类表示“is-a”关系,而接口表示“like-a”关系。
抽象类和接口是Java语言中的两种不同抽象概念。它们的存在为多态性提供了很好的支持,尽管它们之间有很大的相似性。但他们的选择往往反映出你对问题领域的理解。只有对问题域的性质有了很好的了解,才能做出正确合理的设计。
作者:墨雨轩夏
链接:https://www.jianshu.com/p/87441bb9ecc8