抽象类
当编写一些类的时候,都会定义一些方法,在某些情况下,某些父类只是知道子类应该包含某些方法,但是无法准确知道子类方法的实现方式。如图形类的计算周长方法,圆形和矩形拥有不同的计算方法。
但是父类又必须定义一个这样的方法。此时我们可以使用方法来满足该要求。
抽象方法和抽象类
抽象方法和抽象类必须使用abstract修饰符来修饰,有抽象方法的类只能被定义为抽象类,抽象类里面可以没有抽象方法。
抽象方法和抽象类的定义规则如下。
- 抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符修饰,抽象方法不能有方法体。
- 抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,这个抽象类也不能创建实例。
- 抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)五种成分。抽象类的构造器泵用于创建实例,主要用于被子类调用。
- 含有抽象方法的类只能被定义为抽象类。
抽象类的作用
抽象类不能实现实例,只能当成父类来继承。从语义的角度来说,抽象类是多个具体类抽象出来的父类,它具有更高层次的抽象。从多个具有想用特征的类中抽象出来一个抽象类,以这个类作为其子类的模板,从而避免了子类设计随意性。
模板模式在面对对象的软件中很常用,其原理很简单,实现也很简单。下面是使用模板模式的一些简单规则:
- 抽象父类可以只定义需要使用的方法,把不能实现的部分抽象成抽象方法,留给其子类去实现。
- 父类中可能包含需要调用其他方法的方法,这些被调方法既可以由父类实现,也可以由其子类实现。父类里提供的方法只是定义了一个通用的算法,其实现也许并不完全由自身实现,而必须依赖其子类的辅助。
接口
接口的概念
接口时从多个相似类当中抽象出来的规范,接口不提供任何实现。接口体现的时规范和实现分离的设计哲学。
让规范和实现相分离正是接口的好处之一,让软件系统面向接口耦合是一种松耦合的设计。
由于接口一种规范,因此接口的里不能包括构造器和初始化块定义。接口里的成员只能有:成员变量(只能是静态成员变量),接口,方法,内部类。
接口里定义的方法只能时抽象方法、类方法、默认方法或者私有方法,因此如果不是定义默认方法、类方法或者私有方法,系统将会自动为普通方法添加abstract关键词。
接口的继承
接口和类的继承不同,接口可以实现多继承,即一个接口可以有多个直接父类接口。和类继承相似,子接口扩展了某个父接口,将会获得父接口里定义的所有的抽象方法、变量。
一个接口继承多个父类接口的时候,多个父类接口排在extends关键词之后,多个父接口之间以英文逗号隔开。
使用接口
接口不可以用于创建实例,但是可以用来声明引用变量,所声明的引用变量必须是实现了接口的类。除此之外,接口的主要用途就是被实现类实现。归纳起来,接口主要有以下用途:
- 定义变量,也可以用来强制类型转换
- 调用接口中定义的常量
- 被其他类实现
一个类只能继承单个类,但是一个类却可以实现多个接口,继承使用extends实现,实现则是使用implements关键词。当需要实现多个接口的时候,多个接口之间以英文逗号(,)隔开。一个类可以继承一个父类,并同时实现多个接口,implements部分必须放在extends的后面。
一个类实现了一个或者多个接口之后,这个类必须完全实现这些接口所定义的抽象方法。否则该类必须定义为抽象类。
一个类实现某个接口时,该类将会从接口中定义的常量、方法等,因此可以将实现接口看作一种特殊的继承。
实现接口方法时,必须使用public关键词,因为接口当中定义的方法都是public修饰,而实现的类必须要比public更大或者相等,所以必须使用public修饰
接口不能显式的继承任何类,但所有的接口类型的引用变量都可以直接赋给object类型的引用变量,这是向上转型。
接口和抽象类
接口和抽象类很像,他们都具有如下的特征:
- 接口和抽象类都不能实例化,他们都位于继承树的顶端,用于被其他类实现或者继承。
- 接口和抽象类都可以包含抽象方法,实现接口或者继承抽象类的普通子类都必须实现这些方法。
但是接口和抽象类的差别非常大,这种差比主要体现在二者的设计目的之上:
接口作为系统与外界交互的窗口,主要体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务,对于接口的调用者来说,接口主要规定了调用者可以使用哪些服务。当在一个程序当中使用接口时,接口是多个模块之间的耦合标准;当在多个应用程序之间使用接口的时候,接口是多个程序之间的通信标准。
从某种程度上来看,接口类似于整个系统中“总纲”,它制定了系统各模块之间应当遵循的标准,因此一个系统当中的接口不应当经常改变,一旦改变其影响应当是辐射性的。
抽象类则不一样,抽象类作为系统当中多个类的共同法国父类,它所体现的是一种模版式设计。抽象类作为多个类的共同抽象父类。可以被当作系统实现过程中的中间产品,这个中间产品已经实现了系统的部分功能(那些已经提供实现的方法),但是这个产品依旧不能当作最终产品。必须要有更进一步的完善,这种完善可能有几种不同方式。
除此之外抽象类还和接口有以下不同:
- 接口里面只能包括抽象方法,静态方法,默认方法和私有方法,不能为普通方法提供方法实现。
- 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则即可以定义普通成员变量,也可以定义静态常量
- 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是用于其子类调用这些构造器完成初始化。
- 接口里不能包含初始化块;但是抽象类里完全可以包含初始化块。
- 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补java但继承的不足。
面向接口编程
简单工厂模式
有一个场景:假设程序当中有个computer类需要组合一个输出设备,现在有两个选择:直接让computer类组合一个printer,或者让computer类组合一个Output。
如果是第一种方法,直接在computer类中定义printer类的话,不同厂家的printer可能会有不同的实现方法,也需要computer作出相应的调整或者适配。如果某一厂家的printer出现了问题,那么将会影响到所有computer类,修复起来将会十分复杂。
而如果是第二种方法,在computer类中定义output接口,那么它将不需要知道这个类当中的实现方法,直接调用接口所定义的方法即可。
以下定义一个接口
package com.eight.factory;
public interface Output {
public void out();
}
下面定义实现它的类和computer类以及测试类
package com.eight.factory.imp;
import com.eight.factory.Output;
public class HpPrinter implements Output{
@Override
public void out() {
System.out.println("HP Printer is ready");
}
}
package com.eight.factory.imp;
import com.eight.factory.Output;
public class Computer {
private Output printer;
public Computer(Output printer){
this.printer = printer;
}
public void Output(){
this.printer.out();
}
}
import com.eight.factory.Output;
import com.eight.factory.imp.Computer;
import com.eight.factory.imp.HpPrinter;
public class App {
public static void main(String[] args) throws Exception {
Output HP = new HpPrinter();
Computer computer =new Computer(HP);
computer.Output();
}
}
输出结果如下
HP Printer is ready
如果不想要使用HP的printer只需要在代码中将HP的变量指定为其他的类型即可。
命令模式
假设如下场景,某个方法需要某种行为,但是这个行为的具体实现方法无法确定,必须等到执行方法时才能确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素的时候如何处理这些元素,需要在调用这个方法的时候指定某种行为。
这个要求有点奇怪,这个方法不仅需要普通的数据可以变化,甚至方法体也要变化,难道把方法体作为参数传输进去?
可以考虑一个command接口来定义一个方法,用这个方法来封装这个行为。
代码已在本地实现,近期更新至博客