Java 接口和抽象类

原文:http://blog.csdn.net/sunboard/article/details/3831823

1.概述

一个软件设计的好坏,我想很大程度上取决于它的整体架构,而这个整体架构其实就是你对整个宏观商业业务的抽象框架,当代表业务逻辑的高层抽象层结构 合理时,你底层的具体实现需要考虑的就仅仅是一些算法和一些具体的业务实现了。当你需要再开发另一个相近的项目时,你以前的抽象层说不定还可以再次利用 。面对对象的设计,复用的重点其实应该是抽象层的复用,而不是具体某一个代码块的复用

说到了抽象,我就不能不提到曾让我头痛的Java接口和Java抽象类了,这也是本文我想说的重点。

既然面向对象设计的重点在于抽象,那Java接口和Java抽象类就有它存在的必然性了。

Java接口(interface)和Java抽象类(abstract class)代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。OOP面向对象的编程,如果要提高程序的复用率,增加程序 的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程,正确地使用接口、抽象类这些有用的抽象类型作为你结构层次上的顶层。

Java接口和Java抽象类有太多相似的地方,又有太多特别的地方,究竟在什么地方,才是它们的最佳位置呢?把它们比较一下,你就可以发现了。

  1. Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以(就是interface中只能定义方法,而不能有方法的实现,而在abstract class中则可以既有方法的具体实现,又有没有具体实现的抽象方法),这大概就是Java抽象类唯一的优点吧,但这个优点非常有用。如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个 新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点这个在我的另外一篇博客mapreduce 新旧API 区别中有提到类似的问题,在新的mapreduce api中更倾向于使用抽象类,而不是接口,因为这更容易扩展。原因就是上面划线部分所说的。
  2. 一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。(使用抽象类,那么继承这个抽象类的子类类型就比较单一,因为子类只能单继承抽象类;而子类能够同时实现多个接口,因为类型就比较多。接口和抽象类都可以定义对象,但是只能用他们的具体实现类来进行实例化。)
  3. 从第2点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。
  4. 结合1、2点中抽象类和Java接口的各自优势,具精典的设计模式就出来了:声明类型的工作仍然由Java接口承担,但是同时给出一个Java 抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择继承这个抽象类,也就是说在层次结构中,Java 接口在最上面,然后紧跟着抽象类,这下两个的最大优点都能发挥到极至了。这个模式就是“缺省适配模式”。在Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。(A extends AbstractB implements interfaceC,那么A即可以选择实现(@Override)接口interfaceC中的方法,也可以选择不实现;A即可以选择实现(@Override)抽象类AbstractB中的方法,也可以选择不实现)

Java接口和Java抽象类的存在就是为了用于具体类的实现和继承的,如果你准备写一个具体类去继承另一个具体类的话,那你的设计就有很大问题了。Java抽象类就是为了继承而存在的,它的抽象方法就是为了强制子类必须去实现的。

使用Java接口和抽象Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。而不要用具体Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。

2.实例

下面给出一个具体的接口Action,代码如下所示:

[java]  view plain copy
  1. package org.springframework.webflow.execution;  
  2. public interface Action {  
  3.     public Event execute(RequestContext context) throws Exception;  
  4. }  
在这个接口中,定义了一个没有具体实现的方法,方法名叫做execute(),返回类型是Event。如前面第一条所述,接口中的方法都是没有实现的。这些方法的具体实现是在实现(implements)这个接口的类中给出的。

再来看一个实现Action接口的抽象类AbstractAction,代码如下。

[java]  view plain copy
  1. package org.springframework.webflow.action;  
  2.   
  3. import org.apache.commons.logging.Log;  
  4. import org.apache.commons.logging.LogFactory;  
  5. import org.springframework.beans.factory.BeanInitializationException;  
  6. import org.springframework.beans.factory.InitializingBean;  
  7. import org.springframework.util.ClassUtils;  
  8. import org.springframework.webflow.core.collection.AttributeMap;  
  9. import org.springframework.webflow.execution.Action;  
  10. import org.springframework.webflow.execution.Event;  
  11. import org.springframework.webflow.execution.RequestContext;  
  12.   
  13. public abstract class AbstractAction implements Action, InitializingBean {  
  14.   
  15.     protected final Log logger = LogFactory.getLog(getClass());  
  16.   
  17.     public EventFactorySupport getEventFactorySupport() {  
  18.         return new EventFactorySupport();  
  19.     }  
  20.   
  21.     public void afterPropertiesSet() throws Exception {  
  22.         try {  
  23.             initAction();  
  24.         } catch (Exception ex) {  
  25.             throw new BeanInitializationException("Initialization of this Action failed: " + ex.getMessage(), ex);  
  26.         }  
  27.     }  
  28.   
  29.     protected void initAction() throws Exception {  
  30.     }  
  31.   
  32.     protected Event success() {  
  33.         return getEventFactorySupport().success(this);  
  34.     }  
  35.   
  36.     protected Event success(Object result) {  
  37.         return getEventFactorySupport().success(this, result);  
  38.     }  
  39.   
  40.     protected Event error() {  
  41.         return getEventFactorySupport().error(this);  
  42.     }  
  43.   
  44.     protected Event error(Exception e) {  
  45.         return getEventFactorySupport().error(this, e);  
  46.     }  
  47.   
  48.     protected Event yes() {  
  49.         return getEventFactorySupport().yes(this);  
  50.     }  
  51.   
  52.     protected Event no() {  
  53.         return getEventFactorySupport().no(this);  
  54.     }  
  55.   
  56.     protected Event result(boolean booleanResult) {  
  57.         return getEventFactorySupport().event(this, booleanResult);  
  58.     }  
  59.   
  60.     protected Event result(String eventId) {  
  61.         return getEventFactorySupport().event(this, eventId);  
  62.     }  
  63.   
  64.     protected Event result(String eventId, AttributeMap resultAttributes) {  
  65.         return getEventFactorySupport().event(this, eventId, resultAttributes);  
  66.     }  
  67.   
  68.     protected Event result(String eventId, String resultAttributeName, Object resultAttributeValue) {  
  69.         return getEventFactorySupport().event(this, eventId, resultAttributeName, resultAttributeValue);  
  70.     }  
  71.   
  72.     public final Event execute(RequestContext context) throws Exception {  
  73.         Event result = doPreExecute(context);  
  74.         if (result == null) {  
  75.             result = doExecute(context);  
  76.             doPostExecute(context);  
  77.         } else {  
  78.             if (logger.isInfoEnabled()) {  
  79.                 logger.info("Action execution disallowed; pre-execution result is '" + result.getId() + "'");  
  80.             }  
  81.         }  
  82.         return result;  
  83.     }  
  84.   
  85.     protected String getActionNameForLogging() {  
  86.         return ClassUtils.getShortName(getClass());  
  87.     }  
  88.   
  89.     protected Event doPreExecute(RequestContext context) throws Exception {  
  90.         return null;  
  91.     }  
  92.   
  93.     //抽象方法  
  94.     protected abstract Event doExecute(RequestContext context) throws Exception;  
  95.   
  96.     protected void doPostExecute(RequestContext context) throws Exception {  
  97.     }  
  98. }  
在抽象类AbstractAction中,既有具体实现的方法,又有没有具体实现的抽象方法

[java]  view plain copy
  1. //抽象方法  
  2. protected abstract Event doExecute(RequestContext context) throws Exception;  

需要注意的是在抽象类中,如果方法没有具体实现(就是方法后面没有{}),那么必须加上abstract来声明这个方法,而接口中不需要使用abstract来声明(抽象类之所以被称为抽象类,就是因为它包含有抽象方法。含有抽象方法的类叫做抽象类)



--------------------------------------------------------------------------------------------------------------

一.抽象类

  在了解抽象类之前,先来了解一下抽象方法。抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。抽象方法的声明格式为:

1
abstract  void  fun();

  抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。

  下面要注意一个问题:在《JAVA编程思想》一书中,将抽象类定义为“包含抽象方法的类”,但是后面发现如果一个类不包含抽象方法,只是用abstract修饰的话也是抽象类。也就是说抽象类不一定必须含有抽象方法。个人觉得这个属于钻牛角尖的问题吧,因为如果一个抽象类不包含任何抽象方法,为何还要设计为抽象类?所以暂且记住这个概念吧,不必去深究为什么。

1
2
3
[ public abstract  class  ClassName {
     abstract  void  fun();
}

  从这里可以看出,抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情。对于一个父类,如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不同的实现,那么就可以将这个方法声明为abstract方法,此时这个类也就成为abstract类了。

  包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。注意,抽象类和普通类的主要有三点区别:

  1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。

  2)抽象类不能用来创建对象;

  3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。

  在其他方面,抽象类和普通的类并没有区别。

二.接口

  接口,英文称作interface,在软件工程中,接口泛指供别人调用的方法或者函数。从这里,我们可以体会到Java语言设计者的初衷,它是对行为的抽象。在Java中,定一个接口的形式如下:

1
2
3
[ public interface  InterfaceName {
 
}

  接口中可以含有 变量和方法。但是要注意,接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。

  要让一个类遵循某组特地的接口需要使用implements关键字,具体格式如下:

1
2
class  ClassName  implements  Interface1,Interface2,[....]{
}

  可以看出,允许一个类遵循多个特定的接口。如果一个非抽象类遵循了某个接口,就必须实现该接口中的所有方法。对于遵循某个接口的抽象类,可以不实现该接口中的抽象方法。

三.抽象类和接口的区别

1.语法层面上的区别

  1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;

  2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;

  3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;

  4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

2.设计层面上的区别

  1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

  2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

  下面看一个网上流传最广泛的例子:门和警报的例子:门都有open( )和close( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:

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

  或者:

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

  但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:

  1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;

  2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。

  从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface  Alram {
     void  alarm();
}
 
abstract  class  Door {
     void  open();
     void  close();
}
 
class  AlarmDoor  extends  Door  implements  Alarm {
     void  oepn() {
       //....
     }
     void  close() {
       //....
     }
     void  alarm() {
       //....
     }
}

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值