扬帆起航-梦起者

导航

抽象类和接口的联系与区别

接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法

抽象类与接口是Java语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予Java强大的面向对象的能力,他们两者之间对抽象的概念的支持有很大的相似,甚至是可以互换,但是也有其区别。

1.抽象类的定义

   在Java领域,一个类中存在不被实现的抽象方法被换做抽象类,(换一种其他方式理解:在Java领域中,如果一个类没有足够的信息描述这个具体的对象,需要其他具体类来支持它,那么就把这样的一个类称之为抽象类,例如:动物的行为,但是有很多各种动物都有行为,无法确定哪一个动物的行为,需要一个具体的动物来描述其行为。)

  在面向对象领域中由于抽象概念在问题中没有与之对应的具体概念,所以用以表征抽象概念的类是不能被实例化的,同时,抽象类体现的数据抽象的思想,实现了多态的一种机制,抽象类定义了一组抽象方法,用以供他的派生类来实现,所以抽象的设计是建于继承的理念上设计的,也就是说出现了抽象类,即就会有它的派生类继承它实现抽象类中的抽象方法,否则抽象类就毫无意义了。

   Java程序中使用注意的细节:

    1.抽象类中可以有构造函数

    2.抽象类中可以有普通方法,也可以有静态方法

    3.抽象类中可以不存在抽象方法,但是存在抽象方法的一定是抽象类

    4.抽象类中,抽象类是不可以实例化的,必须交由其子类实例化,抽象方法也需交由子类实现

    5.抽象类中可以有成员变量,也可以有main方法运行

    6.子类的抽象方法不能与父类的抽象方法同名

    7.abstract不能与final同时修饰同一个类

    8.abstract不能与static、final、private、native同时修饰同一个方法

    9.一个具体类继承类抽象类,那么就必须实现抽象类中所有的抽象方法,如果只实现了抽象类中的一个抽象方法那么会报错,除非这个具体类变成抽象类

对于上面的细节接下来提供代码参考:

 

 

     2.接口的定义

        接口本质上也是抽象类,接口其实是抽象类的特殊化,接口也是不能被实例化的,真正来说接口其实并不是一个类,它是一个类与类之间的协议,只是提供了一具体实现,并且实现接口是都会有关键字implement,接口是抽象类的延伸,Java为了保证数据的安全性是不能多继承的,所以出现了接口,因为接口可以实现多个接口,弥补了不能继承多个类的情况。

      接口中实现的几个细节:

      1.接口中的方法只能是public修饰

      2.接口不能实例化,只能交由实现接口的具体类实例化

      3.实现接口的具体类必须实现接口中所有的方法,抽象类可以无需全部实现

      4.接口中不能有普通方法和静态方法,也不能有成员变量

      5.接口中可以有常量,例如都是public stastic final 修饰的常量

      6.实现多接口时避免重复方法名

   赋予细节代码参考,出现红叉的是不能出现在接口中的:

 

 

3.接口与抽象类的区别

尽管接口与抽象类有很大的相似处,但是还是有不同之处的,我们就从两个方面来区分差异:

 1.从语法层次

    这个这里就不在重复说明,以上的代码最能看出语法上的不同之处了,我们来看看设计层次的区别吧。

2.设计层次

1、 抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

2、 跨域不同。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。

3、 设计层次不同。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!所以说抽象类往往都是通过重构而来的!但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。

为了更能体现设计层面的说明,我去找了一个例子如下:

 我们有一个Door的抽象概念,它具备两个行为open()和close(),此时我们可以定义通过抽象类和接口来定义这个抽象概念:

      抽象类:

 

  1. abstract class Door{  
  2.     abstract void open();  
  3.     abstract void close();  
  4. }  


      接口

 

  1. interface Door{  
  2.     void open();  
  3.     void close();  
  4. }  


       至于其他的具体类可以通过使用extends使用抽象类方式定义Door或者Implements使用接口方式定义Door,这里发现两者并没有什么很大的差异。

      但是现在如果我们需要门具有报警的功能,那么该如何实现呢?

 解决方案一:给Door增加一个报警方法:clarm();

 

 

  1. abstract class Door{  
  2.     abstract void open();  
  3.     abstract void close();  
  4.     abstract void alarm();  
  5. }  


       或者

 

 

  1. interface Door{  
  2.     void open();  
  3.     void close();  
  4.     void alarm();  
  5. }  


       这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle)—见批注,在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方 法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变而改变,反之依然。

解决方案二

      既然open()、close()和alarm()属于两个不同的概念,那么我们依据ISP原则将它们分开定义在两个代表两个不同概念的抽象类里面,定义的方式有三种:

       1、两个都使用抽象类来定义。

      2、两个都使用接口来定义。

      3、一个使用抽象类定义,一个是用接口定义。

      由于java不支持多继承所以第一种是不可行的。后面两种都是可行的,但是选择何种就反映了你对问题域本质的理解。

      如果选择第二种都是接口来定义,那么就反映了两个问题:1、我们可能没有理解清楚问题域,AlarmDoor在概念本质上到底是门还报警器。2、如果我们对问题域的理解没有问题,比如我们在分析时确定了AlarmDoor在本质上概念是一致的,那么我们在设计时就没有正确的反映出我们的设计意图。因为你使用了两个接口来进行定义,他们概念的定义并不能够反映上述含义。

     第三种,如果我们对问题域的理解是这样的:AlarmDoor本质上Door,但同时它也拥有报警的行为功能,这个时候我们使用第三种方案恰好可以阐述我们的设计意图。AlarmDoor本质是们,所以对于这个概念我们使用抽象类来定义,同时AlarmDoor具备报警功能,说明它能够完成报警概念中定义的行为功能,所以alarm可以使用接口来进行定义。如下:

 

  1. abstract class Door{  
  2.     abstract void open();  
  3.     abstract void close();  
  4. }  
  5.   
  6. interface Alarm{  
  7.     void alarm();  
  8. }  
  9.   
  10. class AlarmDoor extends Door implements Alarm{  
  11.     void open(){}  
  12.     void close(){}  
  13.     void alarm(){}  
  14. }  


       这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实抽象类表示的是"is-a"关系,接口表示的是"like-a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。

 

      批注:

 

   ISP(Interface Segregation Principle):面向对象的一个核心原则。它表明使用多个专门的接口比使用单一的总接口要好。

   一个类对另外一个类的依赖性应当是建立在最小的接口上的。

   一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。

 

    

 

  

posted on 2017-02-20 16:54  扬帆起航-梦起者  阅读(457)  评论(0编辑  收藏  举报