接口和内部类为咱们提供了一种将接口与实现分离的更加结构化的方法。html
抽象类与接口是java语言中对抽象概念进行定义的两种机制,正是因为他们的存在才赋予java强大的面向对象的能力。他们二者之间对抽象概念的支持有很大的类似,甚至能够互换,可是也有区别。java
咱们都知道在面向对象的领域一切都是对象,同时全部的对象都是经过类来描述的,可是并非全部的类都是来描述对象的。若是一个类没有足够的信息来描述一个具体的对象,而须要其余具体的类来支撑它,那么这样的类咱们称它为抽象类。好比new Animal(),咱们都知道这个是产生一个动物Animal对象,可是这个Animal具体长成什么样子咱们并不知道,它没有一个具体动物的概念,因此他就是一个抽象类,须要一个具体的动物,如狗、猫来对它进行特定的描述,咱们才知道它长成啥样。编程
在面向对象领域因为抽象的概念在问题领域没有对应的具体概念,因此用以表征抽象概念的抽象类是不能实例化的。跨域
同时,抽象类体现了数据抽象的思想,是实现多态的一种机制。它定义了一组抽象的方法,至于这组抽象方法的具体表现形式有派生类来实现。同时抽象类提供了继承的概念,它的出发点就是为了继承,不然它没有存在的任何意义。因此说定义的抽象类必定是用来继承的,同时在一个以抽象类为节点的继承关系等级链中,叶子节点必定是具体的实现类。(不知这样理解是否有错!!!高手指点….)安全
在使用抽象类时须要注意几点:ide
一、抽象类不能被实例化,实例化的工做应该交由它的子类来完成,它只须要有一个引用便可。spa
二、抽象方法必须由子类来进行重写。.net
三、只要包含一个抽象方法的抽象类,该方法必需要定义成抽象类,无论是否还包含有其余方法。设计
四、抽象类中能够包含具体的方法,固然也能够不包含抽象方法。code
五、子类中的抽象方法不能与父类的抽象方法同名。
六、abstract不能与final并列修饰同一个类。
七、abstract 不能与private、static、final或native并列修饰同一个方法。、
实例:
定义一个抽象动物类Animal,提供抽象方法叫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(); } } -------------------------------------------------------------------- Output: 猫叫:喵喵... 狗叫:汪汪...
建立抽象类和抽象方法很是有用,由于他们可使类的抽象性明确起来,并告诉用户和编译器打算怎样使用他们.抽象类仍是有用的重构器,由于它们使咱们能够很容易地将公共方法沿着继承层次结构向上移动。(From:Think in java )
接口是一种比抽象类更加抽象的“类”。这里给“类”加引号是我找不到更好的词来表示,可是咱们要明确一点就是,接口自己就不是类,从咱们不能实例化一个接口就能够看出。如new Runnable();确定是错误的,咱们只能new它的实现类。
接口是用来创建类与类之间的协议,它所提供的只是一种形式,而没有具体的实现。同时实现该接口的实现类必需要实现该接口的全部方法,经过使用implements关键字,他表示该类在遵循某个或某组特定的接口,同时也表示着“interface只是它的外貌,可是如今须要声明它是如何工做的”。
接口是抽象类的延伸,java了保证数据安全是不能多重继承的,也就是说继承只能存在一个父类,可是接口不一样,一个类能够同时实现多个接口,无论这些接口之间有没有关系,因此接口弥补了抽象类不能多重继承的缺陷,可是推荐继承和接口共同使用,由于这样既能够保证数据安全性又能够实现多重继承。
在使用接口过程当中须要注意以下几个问题:
一、个Interface的方全部法访问权限自动被声明为public。确切的说只能为public,固然你能够显示的声明为protected、private,可是编译会出错!
二、接口中能够定义“成员变量”,或者说是不可变的常量,由于接口中的“成员变量”会自动变为为public static final。能够经过类命名直接访问:ImplementClass.name。
三、接口中不存在实现的方法。
四、实现接口的非抽象类必需要实现该接口的全部方法。抽象类能够不用实现。
五、不能使用new操做符实例化一个接口,但能够声明一个接口变量,该变量必须引用(refer to)一个实现该接口的类的对象。可使用 instanceof 检查一个对象是否实现了某个特定的接口。例如:if(anObject instanceof Comparable){}。
六、在实现多接口的时候必定要避免方法名的重复。
尽管抽象类和接口之间存在较大的相同点,甚至有时候还能够互换,但这样并不能弥补他们之间的差别之处。下面将从语法层次和设计层次两个方面对抽象类和接口进行阐述。
在语法层次,java语言对于抽象类和接口分别给出了不一样的定义。下面已Demo类来讲明他们之间的不一样之处。
使用抽象类来实现:
public abstract class Demo { abstract void method1(); void method2(){ //实现 } }
使用接口来实现
interface Demo { void method1(); void method2(); }
抽象类方式中,抽象类能够拥有任意范围的成员数据,同时也能够拥有本身的非抽象方法,可是接口方式中,它仅可以有静态、不能修改的成员数据(可是咱们通常是不会在接口中使用成员数据),同时它全部的方法都必须是抽象的。在某种程度上来讲,接口是抽象类的特殊化。
对子类而言,它只能继承一个抽象类(这是java为了数据安全而考虑的),可是却能够实现多个接口。
上面只是从语法层次和编程角度来区分它们之间的关系,这些都是低层次的,要真正使用好抽象类和接口,咱们就必需要从较高层次来区分了。只有从设计理念的角度才能看出它们的本质所在。通常来讲他们存在以下三个不一样点:
一、 抽象层次不一样。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类总体进行抽象,包括属性、行为,可是接口倒是对类局部(行为)进行抽象。
二、 跨域不一样。抽象类所跨域的是具备类似特色的类,而接口却能够跨域不一样的类。咱们知道抽象类是从子类中发现公共部分,而后泛化成抽象类,子类继承该父类便可,可是接口不一样。实现它的子类能够不存在任何关系,共同之处。例如猫、狗能够抽象成一个动物类抽象类,具有叫的方法。鸟、飞机能够实现飞Fly接口,具有飞的行为,这里咱们总不能将鸟、飞机共用一个父类吧!因此说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。对于接口则否则,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。
三、 设计层次不一样。对于抽象类而言,它是自下而上来设计的,咱们要先知道子类才能抽象出父类,而接口则不一样,它根本就不须要知道子类的存在,只须要定义一个规则便可,至于什么子类、何时怎么实现它一律不知。好比咱们只有一个猫类在这里,若是你这是就抽象成一个动物类,是否是设计有点儿过分?咱们起码要有两个动物类,猫、狗在这里,咱们在抽象他们的共同点造成动物抽象类吧!因此说抽象类每每都是经过重构而来的!可是接口就不一样,好比说飞,咱们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,咱们要作的就是事前定义好飞的行为接口。因此说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
(上面纯属我的看法,若有出入、错误之处,望各位指点!!!!)
为了更好的阐述他们之间的区别,下面将使用一个例子来讲明。该例子引自:http://blog.csdn.net/ttgjz/article/details/2960451
咱们有一个Door的抽象概念,它具有两个行为open()和close(),此时咱们能够定义经过抽象类和接口来定义这个抽象概念:
抽象类:
abstract class Door{ abstract void open(); abstract void close(); }
接口
interface Door{ void open(); void close(); }
至于其余的具体类能够经过使用extends使用抽象类方式定义Door或者Implements使用接口方式定义Door,这里发现二者并无什么很大的差别。
可是如今若是咱们须要门具备报警的功能,那么该如何实现呢?
解决方案一:给Door增长一个报警方法:clarm();
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的定义中把Door概念自己固有的行为方法和另一个概念"报警器"的行为方 法混在了一块儿。这样引发的一个问题是那些仅仅依赖于Door这个概念的模块会由于"报警器"这个概念的改变而改变,反之依然。
解决方案二
既然open()、close()和alarm()属于两个不一样的概念,那么咱们依据ISP原则将它们分开定义在两个表明两个不一样概念的抽象类里面,定义的方式有三种:
一、两个都使用抽象类来定义。
二、两个都使用接口来定义。
三、一个使用抽象类定义,一个是用接口定义。
因为java不支持多继承因此第一种是不可行的。后面两种都是可行的,可是选择何种就反映了你对问题域本质的理解。
若是选择第二种都是接口来定义,那么就反映了两个问题:一、咱们可能没有理解清楚问题域,AlarmDoor在概念本质上究竟是门还报警器。二、若是咱们对问题域的理解没有问题,好比咱们在分析时肯定了AlarmDoor在本质上概念是一致的,那么咱们在设计时就没有正确的反映出咱们的设计意图。由于你使用了两个接口来进行定义,他们概念的定义并不可以反映上述含义。
第三种,若是咱们对问题域的理解是这样的:AlarmDoor本质上Door,但同时它也拥有报警的行为功能,这个时候咱们使用第三种方案刚好能够阐述咱们的设计意图。AlarmDoor本质是们,因此对于这个概念咱们使用抽象类来定义,同时AlarmDoor具有报警功能,说明它可以完成报警概念中定义的行为功能,因此alarm可使用接口来进行定义。以下:
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"关系,你们在选择时能够做为一个依据,固然这是创建在对问题领域的理解上的,好比:若是咱们认为AlarmDoor在概念本质上是报警器,同时又具备Door的功能,那么上述的定义方式就要反过来了。
批注:
ISP(Interface Segregation Principle):面向对象的一个核心原则。它代表使用多个专门的接口比使用单一的总接口要好。
一个类对另一个类的依赖性应当是创建在最小的接口上的。
一个接口表明一个角色,不该当将不一样的角色都交给一个接口。没有关系的接口合并在一块儿,造成一个臃肿的大接口,这是对角色和接口的污染。
一、 抽象类在java语言中所表示的是一种继承关系,一个子类只能存在一个父类,可是能够存在多个接口。
二、 在抽象类中能够拥有本身的成员变量和非抽象类方法,可是接口中只能存在静态的不可变的成员数据(不过通常都不在接口中定义成员数据),并且它的全部方法都是抽象的。
三、抽象类和接口所反映的设计理念是不一样的,抽象类所表明的是“is-a”的关系,而接口所表明的是“like-a”的关系。
抽象类和接口是java语言中两种不一样的抽象概念,他们的存在对多态提供了很是好的支持,虽然他们之间存在很大的类似性。可是对于他们的选择每每反应了您对问题域的理解。只有对问题域的本质有良好的理解,才能作出正确、合理的设计。
巩固基础,提升技术,不惧困难,攀登高峰!!!!!!