接口和抽象类都可以声明抽象方法和属性,其抽象方法也能为非抽象类所实现;但它们之间还是有本质的区别,一方面体现在抽象的层次上,另一方面体现在使用中。
用abstract修饰符修饰的类被称为抽象类,它是一种没有具体对象的概念类,并且不能被实例化。
在进行程序设计时,有的时候需要用到抽象的概念。比如我们常说的食物,代表的是我们日常生活中吃的东西的抽象概念。在现实生活中,我们看不到食物的实例,我们看到的只是苹果、巧克力等实例。
再比如交通工具的概念,它在生活中也没有实例,我们能够看到的交通工具的实例是汽车、飞机、轮船等。这种抽象概念的实例没有实际意义。
在Java中,像其他面向对象的编程语言一样,也提供了抽象概念的支持。它引入了所谓的抽象类来模型化现实世界中抽象概念,用抽象类来定义一般的状态和行为。例如,在java.lang包中的Number类就是一个抽象类,它代表数字的抽象的概念,它是我们在程序中使用的所有数字类型的抽象。Number类本身无法创建实例,它是作为integer和float的具体数字类型的抽象父类。
抽象类是一种非完整类,只有抽象类才能包含抽象方法,抽象方法是声明时不提供具体实现的方法。非抽象类不能拥有抽象方法。
当下列几个条件中的任一个成立时,就认为该类中包含抽象方法:包含有一个抽象方法的声明;从直接父类中继承了一个抽象方法;在类的直接超接口中说明或继承了某个抽象方法,但是在这个类中既没有再声明一个方法来实现它、也没有继承一个方法来实现它。
抽象类的声明是在类说明中使用abstract修饰符,如下所示:
abstract class className{
……
}
抽象类不能被初始化,例如下面的程序试图初始化抽象类Server:
abstract class Server{
int status = 0;
int getStatus(){
return status;
}
public class newAbstractClass{
public static void main(String args[]){
new Server();
}
}
编译这个程序将出现如下错误信息。
newAbstractClass.java:11: Server is abstract; cannot be instantiated
new Server();
^
1 error
在这个抽象类中,我们没有声明任何抽象方法,完全可以去掉类声明前的abstract修饰符而将它变成一个普通的类。即使是这样,创建它的实例也是不允许的,因为它是被声明为抽象的。但是创建它的子类的实例是允许的,尽管在其子类中什么都没有做,如:
abstract class Server{
int status = 0;
int getStatus(){
return status;
}
}
class ftpServer extends Server{}
public class newAbstractClass{
public static void main(String args[]){
Server myftpServer = new ftpServer();
System.out.println("Server's status: " + myftpServer.getStatus());
}
}
编译并运行此程序,将产生如下运行结果。
Server's status: 0
可见,一个抽象类的子类,只要其自身不是抽象的,就可以被实例化。当实例化抽象类的某个子类时,该抽象类的构造方法也被调用,同时还将初始化该类的实例变量。所以会产生上面所示的输出结果。
Java既有接口,又有抽象类,它们都有方法和属性。因为接口是简单的未执行的序列以及一些抽象的方法,所以它与抽象类的区别如下:
· 接口不能包含任何可以执行的方法,但抽象类可以。
· 类可以实现多个接口,但只有一个父类。
· 接口不是类分级结构的一部分,但没有联系的类可以执行相同的接口。
【例7-9】抽象类的例子。
//~TestAbstract.java
public abstract class TestAbstract {
public TestAbstract() {}
abstract public int aFunc();
public static void main(String[] args) {
System.out.println("Can Run!");
}
}
本例中,抽象类TestAbstract中的aFunc()方法是抽象的,并没有实现。
什么时候应该使用抽象类?什么时候该使用接口?
先来看这个例子:Dog类和Cat类都是从Animal类继承而来的,为了让它们都实现一个Bark( )方法,可以在Animal类中定义一个Bark( )抽象方法,然后由Dog类和Cat类来实现。
然而,对于其他要继承Animal的类来说,例如Bird类,根本就没有必要实现这个方法。因为这样一方面使Animal的定义变得臃肿,另一方面,诸如Bird类也不得不去实现一个跟自身无关的方法Bark( )。
解决这个问题的方法是使用接口。
先定义一个CanBark接口,接口中声明了Bark( )抽象方法,然后让Cat类和Dog类实现CanBark这一接口。
【例7-10】程序示例。
//Dog.java
interface CanBite { //CanBite接口
public void Bite();
public int TeethCount();
}
interface CanBark { //CanBark接口
public void Bark();
}
class Animal { //Animal基类
String name;
int age;
}
public class Dog extends Animal implements CanBark,CanBite {
public Dog() {
this.name = "Bush";
age = 45;
}
public Dog(String dogname, int dogage) {
this.name = dogname;
this.age = dogage;
}
public void Bark() {
System.out.println("Dog " + name + " is barking!");
}
public void Bite() {
System.out.println("Dog " + name + " is biting!");
}
public int TeethCount() {
if (age <= 0)
return 0;
if (age < 5)
return 28;
return 42;
}
public static void main(String[] args) {
Dog d1 = new Dog("Clinton", 50);
Dog d2 = new Dog();
d1.Bark();
d2.Bite();
}
}
运行结果如下:
Dog Clinton is barking!
Dog Bush is biting!
在第9章多线程中将会学到如何在类中使用多线程,一种办法是让类从Thread继承,另一种办法是在类中实现Runnable接口。
为了方便使用,程序员可以同时使用抽象类实现接口。此时接口中的抽象方法并不需要完全实现,抽象类也不需要重新显式声明接口中的方法为抽象方法;然而,由抽象类所派生的非抽象类必须实现所有的抽象方法,无论来自接口还是抽象类。
【例7-11】程序示例。
//~TestAbsInterface.java
interface ISomething {
public void doOne();
public void doTwo();
}
abstract class Middle implements ISomething {
int count;
public Middle() {
count = 10;
}
public void printCount() {
System.out.println("COUNT is: " + count);
}
public abstract void doThree();
}
public class TestAbsInterface extends Middle {
public TestAbsInterface() {
}
public void doOne() {
count += 1;
}
public void doTwo() {
count += 2;
}
public void doThree() {
count += 3;
}
public static void main(String[] args) {
TestAbsInterface tai = new TestAbsInterface();
tai.printCount();
tai.doOne();
tai.printCount();
tai.doTwo();
tai.printCount();
tai.doThree();
tai.printCount();
}
}
本例中,抽象类Middle实现了接口ISomething,但并没有实现接口中的doOne( )和doTwo( )方法;TestAbsInterface类继承了Middle类,并实现了接口ISomgthing中的抽象方法doOne( )和doTwo( ),实现了Midde类的抽象方法doThree( )。程序的运行结果如下。
COUNT is: 10
COUNT is: 11
COUNT is: 13
COUNT is: 16