您的位置: 网站首页 > 程序开发 > Java程序设计 > 第7章 面向对象的高级特性 > 【7.3 抽 象 类】

7.3 抽 象 类

 

7.3     

接口和抽象类都可以声明抽象方法和属性,其抽象方法也能为非抽象类所实现;但它们之间还是有本质的区别,一方面体现在抽象的层次上,另一方面体现在使用中。

7.3.1  抽象类概念

abstract修饰符修饰的类被称为抽象类,它是一种没有具体对象的概念类,并且不能被实例化。

在进行程序设计时,有的时候需要用到抽象的概念。比如我们常说的食物,代表的是我们日常生活中吃的东西的抽象概念。在现实生活中,我们看不到食物的实例,我们看到的只是苹果、巧克力等实例。

再比如交通工具的概念,它在生活中也没有实例,我们能够看到的交通工具的实例是汽车、飞机、轮船等。这种抽象概念的实例没有实际意义。

Java中,像其他面向对象的编程语言一样,也提供了抽象概念的支持。它引入了所谓的抽象类来模型化现实世界中抽象概念,用抽象类来定义一般的状态和行为。例如,在java.lang包中的Number类就是一个抽象类,它代表数字的抽象的概念,它是我们在程序中使用的所有数字类型的抽象。Number类本身无法创建实例,它是作为integerfloat的具体数字类型的抽象父类。

抽象类是一种非完整类,只有抽象类才能包含抽象方法,抽象方法是声明时不提供具体实现的方法。非抽象类不能拥有抽象方法。

当下列几个条件中的任一个成立时,就认为该类中包含抽象方法:包含有一个抽象方法的声明;从直接父类中继承了一个抽象方法;在类的直接超接口中说明或继承了某个抽象方法,但是在这个类中既没有再声明一个方法来实现它、也没有继承一个方法来实现它

抽象类的声明是在类说明中使用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

可见,一个抽象类的子类,只要其自身不是抽象的,就可以被实例化。当实例化抽象类的某个子类时,该抽象类的构造方法也被调用,同时还将初始化该类的实例变量。所以会产生上面所示的输出结果。

7.3.2  接口和抽象类

1.抽象类与接口的区别

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()方法是抽象的,并没有实现。

2.使用抽象类和接口的场合

什么时候应该使用抽象类?什么时候该使用接口?

先来看这个例子: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接口。

3.抽象类实现接口

为了方便使用,程序员可以同时使用抽象类实现接口。此时接口中的抽象方法并不需要完全实现,抽象类也不需要重新显式声明接口中的方法为抽象方法;然而,由抽象类所派生的非抽象类必须实现所有的抽象方法,无论来自接口还是抽象类。

【例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