学习Java的接口和抽象类

一.概述

    对于面向对象编程而言,抽象是一大特征。在java中可以通过两种形式来体现OOP的抽象:java接口(interface)和抽象类(abstract class).同时要提高程序的复用性,可维护性,可拓展性,面向接口,面向抽象的编程是必不可少的。并且在相近项目的复用时,重点应该是抽象层的复用而不仅仅是代码块。


二.抽象类 abstract class

   解释抽象类之前应该说一下抽象方法:只有声明,没有去具体实.

       abstract viod method();

   抽象方法必须有abstract关键字,而一个类含有抽象方法,则这个类是抽象类,并且需要abstract关键字修饰。在《JAVA编程思想》一书中,将抽象类定义为“包含抽象方法的类”,但是后面发现如果一个类不包含抽象方法,只是用abstract修饰的话也是抽象类。对于这句话,首先这是对抽象类定义的一种补充,但是一个不包含抽象方法的抽象类其实没有任何实际意义(或者说有我并不知道)。就如同烂尾楼,没窗户没门,没法住人,只有个架子,但它的确是楼。

public abstract class className(){
abstract void method1();
abstract void method2();
}
上面已经提到了,但重要的话再说一遍,抽象类需要继承,而且是单一继承,不能实例化

(很多人不理解为什么抽象类要单一继承,我想应该是出于数据安全的考虑)

      在使用抽象类时需要注意几点:

    1.抽象类不能被实例化。(就是不能用new操作new一个新的抽象对象,如Calendar c = new Calendar();

    2.继承类一定要有实现抽象类的方法。因为抽象方法在抽象类里没有实际行为。访问权限只能是public。

    3.子类中的抽象方法不能与父类的抽象方法重名。

    4.包含抽象方法的是抽象类,但是抽象类并不一定仅有抽象方法,可以同普通类逸一样包含成员变量和普通的成员方法。

    5.抽象方法必须是public或者protected,(private不能被子类继承,子类无法实现方法)缺省值为pulic。

    6.如果一个类继承于抽象类则子类必须有实现方法,如果子类没有实现方法,则必须将子类定义为abstract。

三.接口 interface

    在软件工程中,接口泛指提供别人调用的方法或者函数。接口不是类,用于多重继承,不能实例化

public interface interfaceName(){
}
在使用接口过程中需要注意几点:

    1.interface的所有方法访问权限自动被声明为public,且只能是public。(用protected, private,static,final修饰会报编译错误)

    2.接口只能包含static final的成员变量,不过在interface中一般不定义成员变量。而成员方法在接口里只能是抽象方法,访问权限只能是public。可以通过类命名直接访问:implementClass.name.(用private修饰会报编译错误)

    3.接口中都是抽象方法,不能具体实现。

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

    5.在实现多个接口的时候要避免呢方法名的重复。

四.接口和抽象类的区别

(下面这段话取自xw13106209,http://blog.csdn.net/ttgjz/article/details/2960451,没想到什么更好的表达方式。)

    java接口和java抽象类最大的一个区别,在于java抽象类可以提供某些方法的部分实现,而java接口不可以。(就是interface中只能定义方法而不能有方法的实现,而在abstract class中则可以既有方法具体实现,又有没具体实现的抽象方法)。这是java抽象类的优点。如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都会得到这个新方法,而java接口无法做到这点。如果向java接口里加入一个新方法,所有实现这个接口的类就无法成功通过编译,因为你必须让每一个类都再实现这个方法才行。这显然是java接口的缺点。在新的mapreduce api中更倾向于使用抽象类,而不是接口,因为更容易扩展。

    一个抽象类的实现只能是这个抽象类的子类,也就是说这个实现处在抽象类所定义出的继承的等级结构中,由于java语言是单继承,所以抽象类作为类型定义工具的效能大打折扣。在这一点上,java接口有着充分的优势,任何一个实现了一个java接口所规定的方法的类都可以具有这个接口类型,而一个类可以实现任意多个java接口,从而这个类就有了多种类型。(使用抽象类,那么继承这个抽象类的子类类型就比较单一,因为子类只能单继承抽象类,而子类能够同时实现多个接口,因此类型就比较多。接口和抽象类都可以定义对象,但是只能用他们的具体实现类来进行实例化。)由此可以看出java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的类型行为。

    结合抽象类和接口的各自优势,一个经典的设计模式就出来了:声明类型的工作仍由java接口承担,但同时给出一个java抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个java接口,也可以选择继承这个抽象类;也就是说在层次结构中,java接口在最上面,然后紧跟着抽象类,这下两个最大的优点都发挥到了极致。这个模式就是“缺省适配模式”。在java语音API中用了这种模式,而且全都遵循一定的命名规范:Abstract+接口名。(A extends abstract B implements interface C,那么A既可以选择实现(@Override)接口interface C中的方法也可以选择不实现;A既可以选择实现(@Override)抽象类abstract B中的方法,也可以选择不实现)(如果有不清楚可以查看最后的例子)      

     java接口和java抽象类的存在就是为了用于具体类的实现和继承的,如果你准备写一个具体类去继承另一个具体类的话,那你的设计就有很大问题。java抽象类就是为了继承而存在的,它的抽象方法就是为了强制子类必须去实现的。
     使用java接口和抽象类进行变量的类型的类型声明,参数是类型声明,方法的返还类型说明,以及数据类型的转换等。而不要用具体java类进行变量的类型声明,参数是类型声明,方法的返还类型说明,以及数据类型的转换
等。

    1.语法上的区别:
java语言对于抽象类和接口分别给出了不同的定义。下面通过Demo类来说明他们之间的不同之处。
抽象类:

public abstract class Demo {    
    abstract void method1();    
        
    void method2(){    
        //实现    
    }    
} 
接口:
interface Demo {    
    void method1();    
    void method2();    
} 

              从编程的角度来看abstract class和interface都可以用来实现"design by contract"的思想。但具体使用上还是有一些区别:    

    1)abstract class表示的是继承关系,但一个类却可以实现多个接口(这应该是java语言设计者在考虑java对于多重继承的一种折中考虑。) 

    2)在abstract class中我们可以赋予方法默认行为,但在interface的定义中,方法不能拥有默认行为。(不能定义默认行为会导致一个问题,那就是在修改接口的界面的时候会导致维护上的麻烦,比如添加新的方法或者新的参数时,特别是派生类多的时候需要花费大量的时间,但是如果是抽象类,可能只修改定义在abstract class中的默认行为就可以了)
    3)同2,如果不在抽象类中定义默认行为,就会导致同样的方法实现出现在该抽象类的每一个派生类中,违反了"one rule,one place"原则,造成代码重复,不利于以后的维护。2.设计层面区别:
    接口与抽象类在使用方式上有相同之处,但在设计目的上有巨大差别。
    1)接口作为系统和外界的交互窗口,体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式),对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用。
接口是辐射式设计,就好比大多数网游的版本,一旦服务器版本升级,所有用户都得升级,否则没法进入游戏 。所以一个系统中接口不应该轻易改变,否则对整个系统甚至其他系统的影响是辐射性的,导致其派生类都需要重写。
   2)抽象类与接口不同,抽象类作为系统中多个子类的共同父类,它属于模板式设计。就比如A,B页面通过<iframe src="C.html" ></iframe>变成了两个不同的页面,C是公共部分,当需要更改公共部分时,只需要改C就可以了,不需要改动A和B.(总觉得哪里别扭,但是意思没问题)所以如果需要添加新方法,直接在抽象类中添加具体实现,而子类不需要更改。

    2)在abstract class中我们可以赋予方法默认行为,但在interface的定义中,方法不能拥有默认行为。(不能定义默认行为会导致一个问题,那就是在修改接口的界面的时候会导致维护上的麻烦,比如添加新的方法或者新的参数时,特别是派生类多的时候需要花费大量的时间,但是如果是抽象类,可能只修改定义在abstract class中的默认行为就可以了)
    3)同2,如果不在抽象类中定义默认行为,就会导致同样的方法实现出现在该抽象类的每一个派生类中,违反了"one rule,one place"原则,造成代码重复,不利于以后的维护。2.设计层面区别:
    接口与抽象类在使用方式上有相同之处,但在设计目的上有巨大差别。
    1)接口作为系统和外界的交互窗口,体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式),对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用。
接口是辐射式设计,就好比大多数网游的版本,一旦服务器版本升级,所有用户都得升级,否则没法进入游戏 。所以一个系统中接口不应该轻易改变,否则对整个系统甚至其他系统的影响是辐射性的,导致其派生类都需要重写。

   2)抽象类与接口不同,抽象类作为系统中多个子类的共同父类,它属于模板式设计。就比如A,B页面通过<iframe src="C.html" ></iframe>变成了两个不同的页面,C是公共部分,当需要更改公共部分时,只需要改C就可以了,不需要改动A和B.(总觉得哪里别扭,但是意思没问题)所以如果需要添加新方法,直接在抽象类中添加具体实现,而子类不需要更改。

    除此之外,接口和抽象类在用法上存在一些区别:    

   1)接口里不能定义静态方法;抽象类里可以定义静态方法。

   2)接口里不包含构造器,抽象类可以包含构造器,抽象类里的构造器并不是用于创作对象,而是让其子类调用这些构造器来完成属于抽象类的初始化方法。

   3)接口里不能包含初始化块,但抽象类可以包含初始化块。

   4)接口里只能定义静态常量,不能定义其他变量。抽象类既可以定义普通变量,也可以定义静态常量。

   5)接口里不能包含已经提供实现的方法,只能包含抽象方法;抽象类可以包含普通方法。

最后找一个网上多用的例子,门和报警:门都有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() {
      //....
    }
}


猜你喜欢

转载自blog.csdn.net/damagedcurse/article/details/51140726