目录
在面向对象的程序中,抽象性是指当设计一个类时,不让该类面向具体的类,而是面向抽象类或接口(特殊抽象类),即所设计类中的重要数据是抽象类声明的对象,而不是具体类声明的对象,以此来应对需求的变化,提高程序的可维护性。
1. 抽象类与抽象方法
在Java中,当用abstract关键字来修饰一个类时,这个类被称为抽象类。抽象类可以看作是对类的进一步抽象,抽象类的子类可以还是一个抽象类。在面向对象程序中,不能用抽象类来创对象,必须生成抽象类的一个非抽象子类后才能创建对象。在抽象类中,用abstract关键来修饰的方法称为抽象方法。抽象方法只有方法头的声明,而用一个分号“;”来代替方法体的定义,即只定义方法的接口形式,而没有具体的操作。
抽象类可以包含抽象方法,也可以包含非抽象方法,反之,不能在非抽象类中声明抽象方法,即只有抽象类才具有抽象方法。抽象方法的功能需要通过其所在抽象类的非抽象子类的重写来实现。如果一个非抽象类定义为一个抽象类的子类,则该非抽象类必须实现其所继承的抽象类中的所有抽象方法,否则会出现语法错误。
【例1】下面程序演示了抽象类和抽象方法的使用方法。
首先声明了一个几何图形抽象类Geometry,该抽象类包含一个成员变量和一个抽象方法,其代码如下:
源文件Geometry.java
package graph;
/*
* 几何图形(抽象类)
*/
public abstract class Geometry {
String name;//图形名称
//计算面积(抽象方法)
public abstract double getArea();
}
接下来定义抽象类Geometry的两个非抽象子类:矩形Rectangle和圆形Circle,它们的代码如下:
源文件Rectangle.java
package graph;
/*
* 矩形
*/
public class Rectangle extends Geometry{
double length; //长
double width; //宽
public Rectangle() {
name="rectangle"; //name为继承父类的属性
}
public Rectangle(double length,double width) {
this.length=length;
this.width=width;
this.name="rectangle";//name为继承父类的属性
}
//实现父类中的方法
public double getArea() {
return length*width;
}
}
源文件Circle.java
package graph;
/*
* 圆形
*/
public class Circle extends Geometry{
double radius; //半径
public static double PI=3.14;//圆周率
public Circle() {
name="圆形";
}
public Circle(double radius) {
name="圆形";
this.radius=radius;
}
//实现父类中的抽象方法
public double getArea() {
return PI*radius*radius;
}
}
下面的代码的功能是计算矩形和圆形的面积,并在控制台中输出。
源文件Circle.java
package graph;
public class AbstractDemo {
public static void main(String[] args) {
//声明一个抽象类(几何图形)的变量
Geometry geometry;
//指向一个子类(矩形)对象
geometry=new Rectangle(4.0,5.0);
//调用子类(矩形)对象的方法计算面积
System.out.println(geometry.name+"的面积:"+geometry.getArea());
//指向一个子类(圆形)对象
geometry=new Circle(4.5);
//调用子类(圆形)对象的方法计算面积
System.out.println(geometry.name+"的面积:"+geometry.getArea());
}
}
执行AbstractDemo类,程序的运行结果如下:
矩形的面积:20.0
圆形的面积:63.585
虽然不能直接用new运算符创建一个抽象类的子类,但是抽象类的变量可以指向其子类的对象。在上述代码中,抽象类Geometry的变量geometry可以指向其子类Rectangle和Circle的对象。
2. 接口
接口是一种特殊的抽象类,接口中只能包含常量和抽象方法的定义,而没有变量和具体方法的实现。在Java中,接口是通过interface关键字来定义,其语法格式如下:
[访问符] interface接口名 [<类型参数列表>] [extends父接口名列表]{
接口体
}
其中,
访问符 | 接口的访问符只有public和默认两种,用public修饰的接口是公共接口,可以被所有的类和接口所使用,没有任何修饰符的默认接口只能被同一个包中其他类和接口使用。 |
interface | 定义接口的关键字。 |
接口名 | 接口名为合法的Java标识符。 |
类型参数列表 | 如果一个接口包含一个或多个类型变量,则该接口为泛型接口。 |
extends | 明父接口的关键字。与类不同,一个接口可以有多个父接口,当有多个父接口时,用逗号“,”分隔不同的父接口名。 |
接口体 | 接口体中包括常量和抽象方法的声明。由于接口体中只有常量,所以,接口中的成员变量只能定义为static和final型,在类实现接口时不能被修改,成员变量前的final关键字可以省略。由于接口体中所声明的方法为抽象方法,所以没有方法体,访问符只能为public和默认两种,抽象方法前的abstract关键字可以省略。 |
【例2】下面代码演示了接口的定义。
源文件Shape.java
package graph.shape;
/*
* 接口:几何形状
*/
public interface Shape {
double PI=3.14; //圆周率(省略final关键字)
//计算面积(省略abstract关键字)
public double getArea();
}
为了使用接口,需要编写接口的实现类。Java使用implements关键字表示类实现接口,一个类可以同时实现多个接口,接口之间用逗号“,”分隔。在实现一个接口时,在类中可以使用接口中定义的常量,例如,Shape接口中的常量PI。由于接口中的方法都为抽象方法,所以必须在其实现类中加入要实现方法的代码。如果一个接口是从一个或多个父接口继承而来的,则在其实现类中必须加入实现该接口及其父接口中所有抽象方法的代码。
【例3】下面程序演示了接口实现类的定义。
源文件Circle.java
package graph.shape;
/*
* 接口实现类:圆形
*/
public class Circle implements Shape{
double radius; //半径
public Circle() {}
public Circle(double radius) {
this.radius=radius;
}
//计算圆形面积(实现Shape接口中的方法)
public double getArea() {
return PI*radius*radius;
}
}
源文件Triangle.java
package graph.shape;
/*
* 接口实现类:三角形
*/
public class Triangle implements Shape{
double a,b,c;//三个边的长度
public Triangle() {}
public Triangle(double a,double b,double c) {
this.a=a;
this.b=b;
this.c=c;
}
//计算三角形面积(实现Shape接口中的方法)
public double getArea() {
double p=(a+b+c)/2;
return Math.sqrt(p*(p-a)*(p-b)*(p-c));
}
}
接口是Java中的一种重要数据类型,用接口声明的变量称为接口变量,接口变量属于引用型变量。在Java中,可以把实现某一接口的类创建的对象的引用给该接口声明的变量中,那么该接口变量就可以调用被类实现的接口方法,这也称为接口回调。同一接口可以被多个不同的类实现,不同的类在实现同一接口时可能具有不同的实现方法,因此,接口变量在回调接口方法时就可能具有多态性。
【例4】下面的代码演示了接口回调和接口多态性。
package graph.shape;
public class InterfaceDemo {
public static void main(String[] args) {
Shape shape; //声明一个接口变量
shape=new Circle(2);//指向一个圆形实现类对象
System.out.println("圆形面积:"+ shape.getArea());//计算圆形面积
shape=new Triangle(2.0,3.0,4.0);//指向一个三角形实现类对象
System.out.println("三角形面积:"+ shape.getArea());
}
}
执行InterfaceDemo类,程序运行结果为:
圆形面积:12.56
三角形面积:2.9047375096555625
在main()方法中,首先声明一个Shape接口变量shape,然后创建一个该接口的实现类Circle的对象,并将其引用赋给shape,此时,就可以利用shape调用Circle类的getArea()方法来计算圆形面积;接下来,创建一个Shape接口的实现类Triangle的对象,并将其引用赋给shape,此时,shape指向了Triangle类的对象的引用,因此可以调用Triangle类中的getArea()方法来计算三角形面积。
如果一个类声明实现了一个接口,但没有重写接口中的所有方法,那么这个类必须是abstract类。例如,我们可以顶一个实现Shape接口的抽象类Geometry,在该类中可以不用实现Shape接口中的getArea()方法,其代码如下。
public abstract class Geometry implements Shape {
}
接下类,我们可以通过定义Geometry的子类来实现其抽象方法的功能,效果与接口的实现类是一致的。
下面对象抽象类与接口做一个比较:
(1)抽象类和接口都可以有抽象方法;
(2)接口中只能有常量,不能有变量,而抽象类中既可以有常量也可以有变量;
(3)抽象类中也可以有非抽象方法,接口不可以;
(4)抽象类需要通过其子类来实现抽象方法的功能;
(5)接口需要通过其实现类来实现抽象方法的功能。