【Java基础07】抽象类、接口

1 抽象类

1.1  抽象 abstract 

 抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有什么样的属性和行为,并不关注这些行为的细节是什么。

1.2  抽象方法和抽象类

抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。
抽象方法和抽象类的规则如下。

1.抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract 修饰符来修饰,抽象方法不能有方法体。
2.抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,这个抽象类也不能创建实例。
3.抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举) 5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。
4.含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。

定义抽象方法只需在普通方法上增加abstract修饰符,并把普通方法的方法体全部去掉,方法后增加分号。

定义抽象类只需在普通类上增加abstract修饰符即可。一个普通类(不包含抽象方法的类)加abstract修饰符后也将变成抽象类。

抽象类不能用于创建实例,只能当作父类被其他子类继承。

abstract不能用于修饰成员变量,不能用于修饰局部变量,即没有抽象变量、没有抽象成员变量等说法; abstract 也不能用于修饰构造器,没有抽象构造器,抽象类里定义的构造器只能是普通构造器。

抽象类里面并非一定需要抽象方法,但是没有抽象方法,抽象类就没有存在的必要。在以下任一条件成立时,类必须定义成抽象类:
1.类中有至少一个抽象方法。
2.类继承了父类中的抽象方法,如果至少有一个抽象方法在子类中没有被实现,该类必须是抽象类,实现类可以为该抽象类的子类。
3.类实现了某个接口,但没有全部实现接口中的方法。

1.3 抽象类的作用

抽象类体现的就是-种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。

抽象类不能创建实例,它只能当成父类来被继承,抽象类是从多个具体类中抽象出来的的父类,它具有更高层次的抽象,从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板(template),从而避免了设计的随意性,增加了严谨程度。
抽象类体现的是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础.上进行扩展,改造,但子类从整体上会保留抽象类的行为方式。

使用模板样式的简单规则:

1.抽象父类只定义需要使用的某些方法,把不能实现的部分抽象称抽象方法,留给子类去实现。

2.父类中可能包含需要调用其他系列方法的方法,这些被调方法既可以由父类实现,也可以在子类中实现。父类里提供的方法只是定义了一个通用算法,实现也许并不完全由自身实现,必须依赖于其子类的辅助。

2 接口 interface

抽象类是从多个类中抽象出来的模板,如果将这种抽象进行得更彻底,则可以提炼出一种更加特殊的“抽象类”一接口 (intrface), 接口里不能包含普通方法,接口里的所有方法都是抽象方法。Java 8对接口进行了改进,允许在接口中定义默认方法,默认方法可以提供方法实现。

2.1 接口的概念

接口定义了一种规范,某一批类需要遵守,接口不关心这些类的内部状态、也不关心类中方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。

接口时从多个相似类中抽象出来的规范,接口不提供任何实现。接口体现的是规范和实现分离的设计哲学。

接口是抽象方法和常量值的定义集合,是一种特殊的抽象类,这种抽象类中只包含常量和抽象方法的定义,而没有变量和一般方法的实现。

2.2 接口的继承 extends

接口的继承和类继承不一样,接口完全支持多继承,即一-个接口可以有多个直接父接口。和类继承相似,子接口扩展某个父接口,将会获得父接口里定义的所有抽象方法、常量。

一个接口继承多个父接口时,多个父接口排在extends关键字之后,多个父接口之间以英文逗号(,)隔开。下面程序定义了三个接口,第三个接口继承了前面两个接口。

2.2 接口的实现 implements

一个类可以实现一个或多个接口,继承使用extends关键字,实现使用implements关键字。因为一个类可以实现多个接口,这也是Java为单继承特点带来的不足所做的补充。

一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。

一个类实现某个接口时,该类将会获得接口中定义的常量(成员变量)、方法等,因此可以把实现接口理解为一种特殊的继承,相当于实现类继承了一个彻底抽象的类(相当于除了默认方法外,所有方法都是抽象方法的类)。

实现接口方法时,必须使用public 访问控制修饰符,因为接口里的方法都是public的,而子类(相当于实现类)重写父类方法时访问权限只能更大或者相等,所以实现类实现接口里的方法时只能使用public 访问权限。

2.3 接口和抽象类

相同:

1.接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。
2.接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

区别:

1.接口中的方法都是抽象的,而且是公共的,而抽象类中的方法可以有一般方法,并且访问作用域可以是四种修饰符。
2.接口中没有变量,都是常量,而抽象类中含有变量和常量。
3.类和接口的关系impl ements是实现,而类和抽象类的关系是继承extends。
4.类可以实现多个接口,而类只能继承一一个抽象类。

5.接口可以多继承,抽象类只能有一个父类。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

2.4 拓展:面向接口编程

前面已经提到,接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以极好地降低程序各模块之间的耦合,从而提高系统的可扩展性和可维护性。

基于这种原则,很多软件架构设计理论都倡导“面向接口”编程,而不是面向实现类编程,希望通过面向接口编程来降低程序的耦合。下面介绍两种常用场景来示范面向接口编程的优势。

1.简单工厂模式
有一个场景:假设程序中有个Computer 类需要组合一个输出设备, 现在有两个选择:直接让Computer类组合一个Printer,或者让Computer类组合一个Output,那么到底采用哪种方式更好呢?

假设让Computer类组合一个Printer对象,如果有一天系统需要重构,需要使用BetterPrinter 来代替Printer,这就需要打开Computer类源代码进行修改。如果系统中只有一个Computer类组合了Printer还好,但如果系统中有100个类组合了Printer, 甚至1000个、10000 ...将意味着需要打开100个、1000个、10000 个类进行修改,这是多么大的工作量啊!

为了避免这个问题,厂模式建议让Computer类组合一个Output类型的对象,将Computer类与Printer类完全分离。Computer 对象实际组合的是Printer对象还是BetterPrinter 对象,对Computer而言完全透明。当Printer对象切换到BettrPrinter对象时,系统完全不受影响。下面是这 个Computer类的定义代码。

 

上面的Computer类已经完全与Printer 类分离,只是与Output接口耦合。Computer 不再负责创建Output对象,系统提供一个Output工厂来负责生成Output对象。这个OutputFactory工厂类代码如下。

在该OutputFactory类中包含了一个getOutput()方法,该方法返回一个Output实现类的实例,该方法负责创建Output 实例,具体创建哪- 个实现类的对象由该方法决定(具体由该方法中的粗体部分控制,当然也可以增加更复杂的控制逻辑)。如果系统需要将Printer 改为BetterPrinter 实现类,只需让BetterPrinter实现Output接口,并改变OutputFactory类中的getOutput()方法即可。
下面是BetterPrinter 实现类的代码,BetterPrinter 只是对原有的Printer 进行简单修改,以模拟系统重构后的改进。

上面的BetterPrinter 类也实现了Output 接口,因此也可当成Output 对象使用,于是只要把OutputFactory 工厂类的getOutput(方法中粗体部分改为如下代码:

return new BetterPrinter();

再次运行前面的OutputFactory.java程序,发现系统运行时已经改为BetterPrinter对象,而不再是原来的Printer对象。
通过这种方式,即可把所有生成Output对象的逻辑集中在OutputFactory工厂类中管理,而所有需要使用Output对象的类只需与Output接口耦合,而不是与具体的实现类耦合。即使系统中有很多类使用了Printer 对象,只要OutputFactory类的getOutput0方法生成的Output 对象是BetterPrinter对象,则它们全部都会改为使用BetterPrinter 对象,而所有程序无须修改,只需要修改OutputFactory工厂类的getOutput0方法实现即可。

 

猜你喜欢

转载自www.cnblogs.com/discoverspace/p/12262229.html