以前讲了Java的三大特征有封装、继承、多态,这样说其实咱们少讲了一个,那就是抽象性,在平时的教程中认为只有三种特征,由于它们把抽象放到继承里了,认为抽象类是继承的一种,这也使抽象性是不是Java的一大特征具备争议。而Java语言中对抽象概念定义的机制就是抽象类和接口,正因为它们才赋予Java强大的面向对象的功能。他们二者之间对抽象概念的支持有很大的类似,可是也有区别。因此接下来咱们就来学习它们两的使用和区别。(若是在平时面试时遇到问Java有几大特征,咱们就说封装、继承、多态这三特征便可)html
在面向对象的概念中,咱们知道全部的对象都是经过类来描绘的,可是反过来却不是这样。并非全部的类都是用来描绘对象的,若是一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类每每用来表征咱们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不一样,可是本质上相同的具体概念的抽象。好比咱们在建立一个Animal对象时,咱们只知道动物有吃喝拉撒睡等特征,可是殊不知道它究竟是哪种动物,此时这个Animal类是比较抽象的,因此咱们须要一个具体的类来描述该动物,如用狗、猫来对它进行特定的描述,咱们才知道它是什么动物。面试
在Java中用abstract关键字来修饰的类就是抽象类,固然这个关键字也能够用来修饰方法,代表该方法是抽象方法。ide
咱们在使用抽象类的时候须要注意如下几点:学习
①、抽象类不能实例化,必需要由继承它的子类来建立实例。spa
②、抽象方法只有方法的声明,没有方法体。设计
③、抽象类中的抽象方法必需要在子类中重写。code
④、抽象类中既能够有抽象方法,也能够有普通方法,普通方法可不用重写。htm
⑤、只要包含一个抽象方法的类,该类必需要定义成抽象类。对象
⑥、abstract不能用来修饰属性、构造器等结构。blog
⑦、abstract不能与final并列修饰同一个类。
⑧、abstract不能与private、static、final或native并列修饰同一个方法。
而后给出一个简单的示例:
1 public abstract class Animal { 2 //抽象方法 3 public abstract void eat(); 4 //普通方法 5 public void sleep(){ 6 System.out.println("动物须要睡觉..."); 7 } 8 } 9 10 class Cat extends Animal{ 11 12 @Override 13 public void eat() { 14 System.out.println("猫喜欢吃鱼..."); 15 } 16 17 @Override 18 public void sleep() { 19 System.out.println("猫须要睡觉..."); 20 } 21 } 22 23 class Dog extends Animal { 24 25 @Override 26 public void eat() { 27 System.out.println("狗喜欢吃骨头..."); 28 } 29 30 @Override 31 public void sleep() { 32 System.out.println("狗须要睡觉..."); 33 } 34 35 public static void main(String[] args) { 36 Animal a1=new Dog(); 37 a1.eat(); 38 a1.sleep(); 39 40 Animal a2= new Cat(); 41 a2.eat(); 42 a2.sleep(); 43 } 44 }
程序运行结果:
到这里咱们能够看出,抽象类就是为了继承而存在的,若是你定义了一个抽象类,却不去继承它,那么这个抽象类就没有任何的意义了。
接口的英文名称是 interface。接口是属于和类类型同等级别的结构,可是它不是类。却和类类型有着共同点,在接口中能够含有变量、方法。可是须要注意是,接口中的变量会被隐式地指定为public static final变量,而且只能是public static final变量,用private修饰会报编译错误。而接口中的方法会被隐式地指定为public abstract方法,且只能是public abstract方法,若是用其余关键字,如private、protected、static、 final等修饰都会致使报编译错误。也就是说接口中的变量全都是常量,方法都是抽象方法(JDK1.8之前)。从这里能够隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”。
准确来讲接口定义的是一种规范。 例如鸟和蝴蝶都是能够飞的动物,它们都符合 飞行 的规范,因此它们能够实现 飞 这个接口,对于任何其余能够飞行的动物都须要实现该接口。由于它们符合这种规范。
咱们在使用接口的时候一样须要注意如下几点:
①、接口使用interface关键字来定义,而且其子类要implement关键字来实现该接口。
②、接口和抽象类同样不能实例化,必需要由实现它的子类来建立实例。
③、接口中定义的成员变量都是全局常量,系统会在变量前面自动加上public static final,能够经过类命名直接访问:ImplementClass.name。
④、接口中的方法都是抽象方法,可是在JDK1.8之后,能够包含普通方法。
⑤、接口能够多实现,而抽象类只有单继承。
1 public interface Fly { 2 public void fly(); 3 } 4 5 class Bird implements Fly{ 6 7 @Override 8 public void fly() { 9 System.out.println("鸟能够飞行..."); 10 } 11 } 12 13 class Butterfly implements Fly{ 14 15 @Override 16 public void fly() { 17 System.out.println("蝴蝶能够飞行..."); 18 } 19 } 20 21 class Test{ 22 public static void main(String[] args) { 23 Fly bird = new Bird(); 24 bird.fly(); 25 26 Fly butterfly = new Butterfly(); 27 butterfly.fly(); 28 } 29 } 30 //运行结果: 31 //鸟能够飞行... 32 //蝴蝶能够飞行...
接口在JDK1.8以前说里面的方法全都是抽象方法,可是在JDK1.8中,接口新添加了一些特性,咱们能够在接口中添加静态方法和默认方法。固然从技术的角度来看,这彻底合法的,只是看起来违反了接口做为一个抽象定义的理念。
静态方法:使用static关键字来修饰。可使用接口直接调用静态方法而且执行该方法体的内容。咱们常常在相互一块儿使用的类中 使用静态方法。你能够在标准库中找到像Collection / Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法必需要用default关键字来修饰。能够经过子类的实现类来调用。咱们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。好比Collection、Comparator等接口中提供了丰富的默认方法。
1 public interface InterfaceTest { 2 //静态方法 3 public static void method1(){ 4 System.out.println("接口中静态方法..."); 5 } 6 7 //默认方法 8 public default void method2(){ 9 System.out.println("接口中默认方法..."); 10 } 11 12 //默认方法 13 default void method3(){ 14 System.out.println("接口中无修饰默认方法..."); 15 } 16 } 17 //建立一个父类 18 class SuperClass{ 19 public void method2() { 20 System.out.println("父类中默认方法method2..."); 21 } 22 23 public void method3() { 24 System.out.println("父类中默认方法method3..."); 25 } 26 } 27 //建立子类,继承SuperClass,实现InterfaceTest接口 28 class SubClass extends SuperClass implements InterfaceTest{ 29 //重写一个方法method2 30 @Override 31 public void method2() { 32 System.out.println("子类中默认方法method2..."); 33 } 34 } 35 36 class Test{ 37 public static void main(String[] args) { 38 //调用接口中静态方法 39 InterfaceTest.method1(); 40 41 InterfaceTest test=new SubClass(); 42 test.method2(); 43 test.method3(); 44 } 45 }
程序运行结果:
经过运行咱们能够得出如下几点:
①、接口中静态方法只能用接口. 方法来调用,而不能用实例调用。
②、接口中默认方法必需要用子类的实例来调用。
③、若是子类重写了接口中的默认方法,则调用的是子类中重写方法的内容。
④、若是子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写该方法的状况下,默认调用父类中的同名同参数的方法。-->类优先原则。
⑤、若是实现类实现了多个接口(没有继承),而多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的状况下,会报错。-->接口冲突。这就须要咱们在实现类中重写此方法。
⑥、若是须要调用父类中方法或接口中默认方法,父类使用 super . 方法名,接口使用 接口名 . super . 方法名。
这里讲的很是的细,其实咱们只须要了解便可。
如下参考连接:http://www.javashuo.com/article/p-ysawzibf-bo.html
尽管抽象类和接口之间存在较大的相同点,甚至有时候还能够互换,但这样并不能弥补他们之间的差别之处。下面将从语法层次和设计层次两个方面对抽象类和接口进行阐述。
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 abstract class Door { 2 public abstract void open(); 3 public abstract void close(); 4 }
或者:
1 interface Door { 2 public abstract void open(); 3 public abstract void close(); 4 }
可是如今若是咱们须要门具备报警alarm( )的功能,那么该如何实现?下面提供两种思路:
1)将这三个功能都放在抽象类里面,可是这样一来全部继承于这个抽象类的子类都具有了报警功能,可是有的门并不必定具有报警功能;
2)将这三个功能都放在接口里面,须要用到报警功能的类就须要实现这个接口中的open( )和close( ),也许这个类根本就不具有open( )和close( )这两个功能,好比火灾报警器。
从这里能够看出, Door的open() 、close()和alarm()根本就属于两个不一样范畴内的行为,open()和close()属于门自己固有的行为特性,而alarm()属于延伸的附加行为。所以最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。
1 interface Alram { 2 void alarm(); 3 } 4 5 abstract class Door { 6 void open(); 7 void close(); 8 } 9 10 class AlarmDoor extends Door implements Alarm { 11 void oepn() { 12 //.... 13 } 14 void close() { 15 //.... 16 } 17 void alarm() { 18 //.... 19 } 20 }