在java的类体系中,有两个比较特殊的抽象体--抽象类和接口。抽象体并不可以拥有具体的对象(而且接口甚至不是类),但是它们却是在java设计领域非常重要的两个概念,很多优秀的设计模式都是基于这两个概念的,尽管我们在进行应用开发时可能用的比较少,但是在真正的大型项目的架构开发中,他们是用来确定整个项目的整体框架的,是必不可少的。下面对抽象类以及接口的一些基本语法知识进行整理,用以备忘。
一、什么是抽象类?什么是接口?
首先,前面也说了,抽象类和接口都不可直接实例化,它很多时候只是在声明引用的时候拿来实现多态。
1、抽象类。
抽象类,具体的声明语法如下:
//这是一个抽象类,通过abstract声明
abstract class AbstractClass{
//抽象类里面可以有非抽象方法
public void method1(){
//这是个非抽象方法,在子类中(子类为非抽象类时)可以直接拿来用
System.out.println("我在抽象类里,但是我是非抽象方法");
}
//下面的是抽象方法,它没有方法实体,只有声明
public abstract void method2();//如果子类不是抽象类,则必须实现该方法
//private abstract void meethd3();//抽象类中的方法不可被private修饰
}
显然:抽象类内部可以有非抽象方法(就算所有方法是非抽象方法也符合语法规则),子类继承抽象类后,如果子类不是抽象类,则必须实现所有抽象方法而且内部不能有抽象方法的声明。另外,抽象类的方法不能声明为private;其实这也很好理解:抽象方法本来就应该能被子类访问得到,如果设置为private,则子类访问不了,则它存在的意义就没有了。
2、接口。
接口,不是java的类,它只是声明了该接口(interface)所需要的抽象方法。具体的语法知识可以看下面的代码说明:
//这是一个接口,通过interface关键字进行声明
interface InterfaceTest{
//这是一个接口内的方法,它类似抽象类的抽象方法,没有具体的实现,只有方法的声明部分(public可以省略)
public void method1();
//下面这种写法也是可以的,不过,接口的方法默认就是public和abstract而且必须是public 和 abstract
abstract void method2();
//下面的方法声明有问题
//private void method3();//注意方法修饰必须为public或者默认
}
显然:接口的语法具有以下要求:默认修饰符是public和abstract,而且必须只能由这两个修饰符修饰(当然,这里是指权限修饰以及是否抽象修饰),这也很好理解:接口本来就需要被实现的,而且接口出现本来就是为了多态操作,只有保证外界代码可以访问到接口中的方法,方可实现多态,接口才是有意义的。
二、抽象类与接口的具体用处
1、多态与抽象类、接口。首先,抽象类和接口都是为了在组织架构时实现代码良好的解耦而产生的概念,它们最终目的就是保证程序的可扩展性,使程序块在不同外界环境(传入对象参数不同)下,相同方法显示出多种不同的行为特征。具体看以下代码:
//抽象类与接口的多态说明代码
//这是个动物抽象类,现实中没有正真动物这个实体的,只不过是好像猫,狗等动物的抽象体罢了
abstract class Animal{
public abstract void eat();
public abstract void sleep();
}
//这个是飞的行为的接口,由于动物中不是所有动物都会飞,所以考虑将它独立成接口,需要的动物实现接口就可以
interface FlyAction{
void fly();
}
//这个是狗类,它继承了动物类
class Dog extends Animal{
public void eat() {
System.out.println("我是dog的eat方法,我喜欢边吃边拉");
}
public void sleep() {
System.out.println("我是dog的sleep方法,我喜欢边睡觉边流口水");
}
}
//这个是鸟类,它继承了animal的类,同时也实现了FlyAction的接口
class Bird extends Animal implements FlyAction{
public void fly() {
System.out.println("我是鸟,我会飞,而且飞得很快");
}
public void eat() {
System.out.println("我是鸟,我吃得很少");
}
public void sleep() {
System.out.println("我是鸟,我睡觉的姿势很酷");
}
}
//这个是飞机类,它也会飞,但是它不是动物
class Airplane implements FlyAction{
public void fly() {
System.out.println("我是飞机,我飞得又高又快");
}
}
//下面这个是测试类
public class Test{
FlyAction somethingCanFly = null;
Animal animal = null;
public void setSomethingCanFly(FlyAction somethingCanFly) {
this.somethingCanFly = somethingCanFly;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
//这个是具体应用类,通过操作接口便可操作对应对象的方法,实现多态
public void fly(){
somethingCanFly.fly();
}
//这个是具体应用类,通过操作抽象父类操作方法,可以轻易实现多态
public void eat(){
animal.eat();
}
public static void main(String[] args) {
AbstractInterface test = new AbstractInterface();
test.setAnimal(new Bird());//这里如果换成传进来的是飞机对象,则非的方式变是飞机飞的方式
test.fly();//这里便是鸟的飞,具体怎么飞是上一句传进来的参数控制的,接口实现了多态
test.setAnimal(new Dog());//这里如果换成传进来的是鸟的对象,则吃的方式便是鸟吃的方式,抽象类实现了多态
test.eat();//这个是animal的吃,具体的吃也是由传进来的对象参数觉得的,抽象类实现了多态
}
}
观察代码,其实抽象类和接口都体现了这样一种思想:通过操作抽象的引用,便可在具体执行过程表现出不同的方法行为,这就是多态的具体表现。
更多多态方面的知识,之后的设计模式方面的整理会提到,这里大概就将这么多啦。
2、接口or抽象类(继承or实现接口)?
上面的代码估计大家也看到了,接口和抽象类都可以实现多态,他们的作用大体上都是通过抽象的引用操作实际的不同对象,很多时候在显式中,可能两种方式都可以达到目的,那该如何抉择呢?
其实,很容易想到,java并不支持多继承,但是可以利用接口实现类似多继承的效果。所以,抽象类,它可以复用代码,但是却不可以多继承,而接口却很多时候承担着类似于多继承的使命,但是显然它所有方法都是抽象的,代码并不能达到复用的效果。所以,抽象类还是接口,还是要回到架构设计上来:如果子类和对应的抽象类有从属关系(狗是一种动物),则采用继承吧,因为继承要求父类所有方法对子类都是有用的;但是如果好像类似上面的例子:飞机和鸟都会飞,但是他们是两种本质不同的东西,但恰恰他们都有共同的特点:飞的这个行为,所以这是使用接口是合理的,因为,很多时候接口充当类似于一个附加功能或者行为的概念。
好吧,具体更多面向接口和抽象的编程在设计模式中再具体总结吧!