最近学到抽象类和接口的知识,看了李兴华老师讲解的视频及一些前辈博客,这里做一下总结,方便以后复习
抽象类和接口是整个Java面向对象的核心部分,他们之间有共性也有区别,下面主要从以下方面逐一总结
一.抽象类
抽象方法:只声明而未实现(没定义方法体)的方法称为抽象方法,抽象方法必须使用abstract关键字声明
abstract void fun();
抽象类:如果一个类中包含抽象方法,那么此类就是抽象类,抽象类也必须使用abstract关键字声明,抽象类是为继承而生,所以必须要有子类继承,不然没意义
abstract class Demo{ //抽象类
String name;
public void print(){
System.out.print("hello");
}
abstract void fun(); //抽象方法
}
抽象类和普通类相比,只是增加了抽象abstract class的声明,和增加了抽象方法而已,其他的属性、方法定义结构都一样。
但是抽象类不能被实例化,也就是说,如果把一个类声明为abstract,那么该类不能创建对象,因为抽象类中的抽象方法没有实现,调用没有意义。如下:
abstract class Demo{ //抽象类
String name = "Jack";
public void print(){
System.out.print("hello");
}
abstract void fun(); //抽象方法
}
public class AbstractDemo{
public static void main(String[] args){
Demo demo = new Demo();
}
}
是错误的,不能创建抽象类的对象。虽然不能创建抽象类的对象,但是却可以定义一个抽象类的对象变量,然后引用非抽象子类的对象。
好比我们定义一个宠物抽象类,让小猫继承它,虽然我们不能创建宠物的对象,但是可以创建小猫对象,让宠物引用指向小猫对象,如下:
abstract class Pet{ //抽象类
abstract void eat(); //抽象方法
}
class Cat extends Pet{
public void eat(){
System.out.println("吃肉");
}
}
public class AbstractDemo{
public static void main(String[] args){
Pet pet = null; //定义抽象类的对象变量
pet = new Cat(); //创建非抽象子类的对象
pet.eat();
}
}
还需注意的是:
1.一个类即使没有抽象方法,也可以声明为抽象类,这样做的目的只是为了不让该类创建对象。
2.抽象类必须要有子类,并且子类要覆写所有的抽象方法,否则该子类还是一个抽象类。
3.抽象类不能声明为final,因为final声明的类不能有子类,而抽象类必须要有子类,矛盾。
4.抽象方法不能声明为private,因为private声明的方法不能被子类继承,子类无法实现此方法。抽象方法默认为public
另外,抽象类虽然不能实例化,但可以定义构造方法,因为抽象类有子类 ,子类在实例化之前会先实例化父类,调用其构造方法,当然也可以通过super显示调用
abstract class Pet{ //抽象类
public Pet(){
System.out.println("抽象类构造方法");
}
abstract void eat(); //抽象方法
}
class Cat extends Pet{
public Cat(){
System.out.println("子类构造方法");
}
public void eat(){
System.out.println("吃肉");
}
}
public class AbstractDemo{
public static void main(String[] args){
Pet pet = null; //定义抽象类的对象变量
pet = new Cat(); //创建非抽象子类的对象
pet.eat();
}
}
二.接口
当一个类中全部是由抽象方法和全局常量组成的时候,就可以将这个类定义成一个接口了,接口使用interface关键字声明
interface Demo{
public static final String NAME = "Jack";
public abstract void print();
}
对于接口来讲,因为其在定义的时候已经确定了其由全局常量和抽象方法组成,所以可以简化:
interface Demo{
String NAME = "Jack";
void print();
}
编译时会自动加上修饰关键字。
另外注意:所有的修饰符在接口中是否添加本身是没有任何意义的,而且接口中的方法全部都属于公共的方法操作(public)。
接口的使用中必须有子类,子类必须覆写全部抽象方法,通过implements实现。
1.一个子类可以实现多个接口,如下
class 类名 implements 接口A,接口B,[......]{
}
interface A{
String INFO = "hello world";
void print();
}
interface B{
void info();
}
class C implements A,B{
public void print(){
System.out.println("INFO = "+INFO);
}
public void info(){
}
}
public class InterfaceDemo{
public static void main(String[] args){
C c = new C();
c.print();
}
}
2.一个子类可以同时继承抽象类并实现接口,而且必须先继承抽象类之后再实现接口
class 类名 extends 抽象类 implements 接口A,接口B,[......]{
}
abstract class A{
public String INFO = "hello world";
abstract void print();
}
interface B{
void info();
}
class C extends A implements B{
public void print(){
System.out.println("INFO = "+INFO);
}
public void info(){
}
}
public class InterfaceDemo{
public static void main(String[] args){
C c = new C();
c.print();
}
}
那么该子类需要覆写抽象类和接口的所有抽象方法。
3.一个抽象类可以实现接口,那么该抽象类的子类必须同时覆写抽象类和接口的所有抽象方法,注意与第二点的区别
abstract class A implements B{
public String INFO = "hello world";
abstract void print();
}
interface B{
void info();
}
class C extends A{
public void print(){
System.out.println("INFO = "+INFO);
}
public void info(){
}
}
public class InterfaceDemo{
public static void main(String[] args){
C c = new C();
c.print();
}
}
本质上和第二点是一样。4.一个接口可以同时继承(extends)多个接口
interface A{
String INFO = "hello world";
void printA();
}
interface B{
void printB();
}
interface C extends A,B{
void printC();
}
class D implements C{
public void printA(){
}
public void printB(){
}
public void printC(){
}
}
注意:类实现(implements)接口,接口继承(extends)接口三.抽象类和接口的区别
这部分参考了前辈:
http://www.cnblogs.com/dolphin0520/p/3811437.html
http://peiquan.blog.51cto.com/7518552/1271610
的博文
用法上的区别:
1.接口里面不能定义静态方法,抽象类可以定义静态方法
2.接口里不包含初始化块,抽象类可以包含初始化块
3.接口里不含有构造方法,抽象类可以包含构造器。抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
4.接口里不能包含已经实现的方法,只能有抽象方法,而抽象类可以同时包含
5.接口里只能声明静态常量,抽象类可以定义普通变量,也可以定义静态常量
6.一个类只能继承一个抽象类,而一个类却可以实现多个接口。
设计上的区别:
抽象类所代表的是“is-a”的关系,而接口所代表的是“like-a”的关系。
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( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:abstract class Door {
public abstract void open();
public abstract void close();
}
或者:
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接口。
interface Alram {
void alarm();
}
abstract class Door {
void open();
void close();
}
class AlarmDoor extends Door implements Alarm {
void oepn() {
//....
}
void close() {
//....
}
void alarm() {
//....
}
}
参考资料:
李兴华JAVA视频讲解
http://www.cnblogs.com/dolphin0520/p/3811437.html
http://peiquan.blog.51cto.com/7518552/1271610
http://dev.yesky.com/436/7581936.shtml