接口与抽象类这两个概念相信大家都已经十分熟悉了,几乎每一次面试的时候,面试官总是问接口与抽象类的区别,这两个概念的确有很多相似之处,下面我们就来理一下Java中的接口和抽象类。
由于Java不支持多继承,每个类只能有一个父类,因此Java使用了接口,一个类可以实现多个接口。接口的定义很简单,使用interface关键字,接口中可以定义常量和方法,但是在接口中只能定义方法的声明,而不能实现。类可以通过关键字implements实现接口,如果实现多个接口,中间用逗号隔开。如果某个普通类一旦实现了接口,那么就要实现该接口中定义的所有方法,但是抽象类例外,如果一个abstract修饰的类实现某个接口,可以不用实现该接口定义的所有方法。
接口也可以被继承,可以通过关键字extends声明一个接口是另一个接口的子接口,由于接口中的方法和常量都是public的,子接口将继承父接口中的全部方法和常量。
再来看抽象类的概念,这个比较简单,用abstract关键字修饰的类就是抽象类。抽象类中可以有抽象方法,也可以没有抽象方法,如果有抽象方法的话(类似接口方法,也是只定义,不实现),那么其子类必须要实现该抽象类中定义的抽象方法。注意的是,抽象类中的抽象方法不能用final来修饰,因为final修饰的方法不能被重写。抽象类只关心子类有什么功能,具体怎么实现都交给子类去完成。
接下来通过具体的示例来说明问题,加深对接口和抽象类的认识。
先定义一个接口,该接口定义了四个方法:
<span style="font-size:14px;"><span style="font-size:10px;">public interface AinaListener {
public void first();
public void second();
public void third();
public void forth();
}</span></span>
然后定义一个类实现该接口,那么该类必须实现接口定义的所有方法:
<span style="font-size:14px;"><span style="font-size:10px;">/**
* 如果类直接实现接口,则必须实现接口的所有方法
*
*/
public class ClassOne implements AinaListener{
@Override
public void first() {
// TODO Auto-generated method stub
}
@Override
public void second() {
// TODO Auto-generated method stub
}
@Override
public void third() {
// TODO Auto-generated method stub
}
@Override
public void forth() {
// TODO Auto-generated method stub
}
}</span></span>
再定义一个抽象类也实现该接口,抽象类可以不必实现接口中的所有方法,下面的示例中只实现了前两个方法:
<span style="font-size:14px;"><span style="font-size:10px;">/**
* 抽象类可以不必实现接口的所有方法
*
*/
public abstract class AbstractClassTest implements AinaListener {
@Override
public void first() {
// TODO Auto-generated method stub
}
@Override
public void second() {
// TODO Auto-generated method stub
}
}</span></span>
如果父类已经实现了某个接口,那么子类继承父类后,就不必再使用implements关键字去实现接口,下面我们再写一个ClassTwo来继承上面的ClassOne,可以看到在ClassTwo中不必实现接口定义的所有方法,可以随意实现哪几个或不实现,示例中实现了第一个和第三个方法:
<span style="font-size:14px;"><span style="font-size:10px;">/**
* 不直接实现接口,而是继承一个实现了接口的类,可以有选择地实现接口中的方法
* @author Administrator
*
*/
public class ClassTwo extends ClassOne{
@Override
public void first() {
// TODO Auto-generated method stub
super.first();
}
@Override
public void third() {
// TODO Auto-generated method stub
super.third();
}
}</span></span>
有时候我们不想实现接口中所有的方法,因此可以先用一个父类实现接口,然后各个子类去继承父类,然后有选择地实现接口中的方法。这种需求也就导致到底是要用接口还是要用抽象类。比如一个抽象类,也有first、second、 third、 forth四个抽象方法,也许某个子类不需要实现四个方法,只需要其中一个或两个就可以,那么使用抽象类显然不合理。如果你说把四个方法都定义在一个接口里,子类实现的话也必须四个方法都要实现,是这样的,但是上面的接口例子只是为了举例,我们可以把四个方法分别定义在四个接口里,因为类可以实现多个接口,我们只需要给特定的类实现它所需要的接口就行了,这样是不是很灵活了?在这方面,抽象类是做不到的,这是因为抽象类定义的抽象方法,子类都必须实现,且类不能多继承。
说到接口,就不得不提一下接口回调,接口回调是指可以把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量中,那么该接口变量就可以调用被类实现的接口中的方法了。
再看一个示例,先定义一个“吃”接口,再定义一个Dog类和Test测试类:
<span style="font-size:14px;"><span style="font-size:10px;">public interface Eat {
void eatFood();
}</span></span>
<span style="font-size:14px;"><span style="font-size:10px;">public class Dog implements Eat {
@Override
public void eatFood() {
// TODO Auto-generated method stub
System.out.println("小狗吃骨头");
}
}</span></span>
<span style="font-size:14px;"><span style="font-size:10px;">public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Eat eat; //接口变量
eat = new Dog();//把实现接口的类的对象引用赋给接口变量
eat.eatFood(); //接口回调
}
}</span></span>
上面的代码示例简单地演示了接口的回调。