Java中抽象类与接口区别详述

abstract class和interface是Java中对抽象进行定义的两种机制,这两种机制在对抽象定义的支持方面有很大的相似性,有时甚至可以相互替换。因此,对于到底使用哪种机制进行问题的抽象时,很容易产生困惑。

1.Java语言中的抽象
在面向对象的概念中,所有的对象都是通过类来进行描述的,如果一个类中没有足够的信息来描述一个具体的对象,这样的类就是抽象类( 注:这里的抽象类并不是指abstract class,而一个统称 )。在代码设计时,抽象类是我们 对问题领域进行抽象,并且提取出其本质的结果。
在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一组固定的行为的抽象描述,但这组行为确可以有若干个实现方式。这个抽象的描述就可以视为抽象类,每个可能的具体实现就是其派生类。模块可以操作抽象类,而且它依赖一个固定的抽象类,因此抽象类是不可以改变的。
抽象类是面向对象设计的原则中OCP(Oper-Closed Principle)的关键所在。

2.两种机制在语法层面的区别
abstract class中可以有自己的数据成员,也可以有非abstract的成员方法;而对于Intetface的实现中只能有静态的、不可被修改的数据成员(static final类型),而其中的所有方法均是abstract的。可以说,interface是一种特殊的abstract class。
abstract class表示的是一种继承关系,一个类只能有一个继承关系;而一个类却可以实现多个interface接口。这可能也是Java语言设计都对多重继承的一种折衷的考虑吧。
abstract class中,我们可以赋予方法默认行为;但在Interface中这是不可以的。在抽象类的定义的方法的默认行为可以被其所有派生类所继承,对于维护中的修改,我们只需修改这个默认行为就可以在所有派生类中生效,避免了很多不必要的工作。同样,如是不能在抽象类中定义默认行为,这样就违反了 “one rule, one place” 的原则,造成代码重复,而且不易维护。

3.在设计理念层面的区别
在前面我们已经提过,abstract class是一种继承关系,父类和子类间存在一种is-a的关系,即父类和子类在本质上应该是相同的。而interface更加偏向于是一种like的关系,所以在程序设计的时候我们要尽可能的考虑以上两点的不同。
通过以下例子我们来进一步说明这个问题:
在我们的问题领域有一个关于Door的抽象概念,它具有两个行为open和close,此时我们可以使用这两种机制来定义这个抽象模型。
abstract  class  Door {

        abstract  void  open();
        abstract  void  close();
}
interface  Door {

        void  open();
        void  close();
}
其它具体的Door可以通过继承或是实现接口的方式来定义Door,从这个角度来看这两种机制之间没有明显的差别。
但当我们要求Door具有报警功能时,我们该如何解决这个问题?若根据上面的方法有:
abstract  class  Door {

     abstract  void  open();
     abstract  void  close();
     abstract  void  alarm();
}
interface  Door {

     void  open();
     void  close();
     void  alarm();
}
这种设计方法违反了面向对象设计原则中的ISP(Interface Segregation Principle)原则,把Door的固有行为同“报警器”的行为混合在了一起。
我们可以看出open、close和alarm属于两个不同的概念,因此需要两个不同的抽象模型来描述。要知道Java中我们只能继承一个类,因此这两个概念都有abstract class来描述是不可以的。在这里,我们需要对AlarmDoor的本质进行考虑,它究竟是一个Door还是Alarm,如果是Door,我们将Door抽象成abstract class,而将alarm抽象成interface,即" Alarm Dooor is a door which like alarm"。即,
abstract   class  Door {

        abstract   void  open();
        abstract   void  close();
}
interface  Alarm {

     void  alarm();
}
这种实现方式基本上能够正确地反映出我们对于问题领域的理解,正确地揭示出我们的设计意图。

总结:
1,抽象类和接口都不可以实例化,只能对其子类或是实现该接口的类进行实例化;
2,抽象类需要继承,而接口需要实现;
3,抽象类即可以有方法声明也可以方法实现,而接口中只能有方法的声明;
4,抽象类中可以定义变量,而接口中只能定义公共静态的常量;
5,抽象类的子类必须实现抽象类中的所有方法,如果子类不能实现父类中的全部方法,那么该子类也只能是一个抽象类。同样,实现一个接口的类,如果不能实现接口的全部方法,那么这个类也只能是抽象类;
6,抽象方法只能声明,不能实现。而且abstract void abc()不能写成abstract void abc() {};
7,抽象类里可以没有抽象方法;
8,如果一个类里有抽象方法,那么它只可能是一个抽象类;
9,抽象方法需要被实现,所以它不能是静态的、也不可以是私有的;
10,接口可以继承接口,而且接口可以继承多个接口,而类只可以单根继承;
——在Java中不允许多重继承的主要原因是:如果类A同时继承类B和类C,而类B和类C同时拥有一个d方法,那类A就无法得知该继承哪个类的d方法了。但是,在接口中不存在这样的问题,接口中全部都是抽象方法,继承谁都无所谓。


基于以上描述可知,抽象类是不可以实例化的,可以说抽象类只是为了继承而存在的,也就是只能实例化抽象类的子类。 这是毋庸置疑的 ,但在实际处理问题时也可以有很多变通的方法:
abstract  class  E {
        void  doSomething() {
            System.  out .println( "hello !"  );
      }
}
public  class  Abstract {
        public  static  void  main(String[] args) {
            E e =  new  E() {
            };
            e.doSomething();
              // 输出hello !
      }
}
这里是利用了一个匿名内部类来继承了抽象类E,匿名类是没有名称的类,创建时使用new语句的一部分来声明,所以本质上是声明了一匿名类并且继承了抽象了,实例化的对象也是匿名类的对象。其中,{}内是匿名类的实现细节,在本体例中,抽象类中的方法都已实现,大括号内为空就可以,若抽象类中还有抽象函数,那么这个匿名类就必须实现这个抽象函数。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值