Java学习之旅第二季-15:抽象类
15.1 抽象类
开发时想要创建一个仅定义所有子类都将共享的通用形式的父类,而将具体实现细节的工作留给每个子类。这样的类确定了子类必须实现的方法的声明,但自身并不提供一个或多个这些方法的实现。这种情况出现的一种方式是当父类无法为某个方法创建有意义的实现时。
例如,我们想创建一个表示形状的类,并提供一个计算其面积的方法,但是由于几何中的形状有很多,没有办法实现所有形状的面积计算,这样就可以设计一个抽象的父类,然后声明一个不实现具体计算面积的方法。而是由表示不同形状的子类实现。这种需求可以使用抽象类和抽象方法实现。
抽象类就是使用关键字 abstract 修饰的类,类中的方法如果也使用 abstract 修饰,则称为抽象方法。
下面我们按照这个思路,先声明一个抽象的类Shape,用来表示形状的超类,在其中声明一个抽象方法:
public abstract class Shape {/*** 计算面积** @return 面积*/public abstract double getArea();
}
我们注意到抽象方法是没有方法体的,结尾直接以分号结尾,因此不会由超类实现。因此,子类必须重写它并提供具体的计算面积的实现。在声明子类之前,先对抽象类及抽象方法的语法做一些说明。
抽象类的特征:
-
抽象类可包含普通类允许声明的成员,比如属性,代码块,构造方法(不能全是private修饰的),实例方法,静态方法等
-
抽象类不能被实例化,即使该抽象类不包含抽象方法
-
抽象类不一定有抽象方法,但抽象方法一定在抽象类中
-
抽象类和抽象方法不可使用final修饰,final修饰的类不能被继承,final修饰的方法不能被重写,这与抽象类和抽象方法使用方式冲突
-
构造方法,静态方法,私有方法不可为抽象方法
15.2 继承抽象类
要继承抽象类,子类仍然使用关键字 extends,但是这里分两种情况:
1、如果子类是具体(非抽象)类则需要实现所有的抽象方法
/*** 长方形** @author 老谭*/
public class Rectangle extends Shape {private double height; // 高度private double width; // 宽度public Rectangle(double height, double width) {this.height = height;this.width = width;}@Overridepublic double getArea() {return height * width;}
}
上述类声明为普通的类,它继承了抽象的父类,所以必须实现父类中的抽象方法getArea,否则就会有语法错误。类似的,如果计算一个正方形的面积:
/*** 正方形** @author 老谭*/
public class Square extends Shape {private double size; // 边长public Square(double size) {this.size = size;}@Overridepublic double getArea() {return size * size;}
}
下面使用代码进行测试:
Rectangle rectangle = new Rectangle(2, 7);
System.out.println(rectangle.getArea());
Square square = new Square(5);
System.out.println(square.getArea());
目前的代码还不足以体现抽象类的好处,随着后续知识点的展开,我们会见到它在多态方面的作用。
2、如果子类是抽象类则不一定,比如下面的子类:
/*** 不规则形状** @author 老谭*/
public abstract class Irregular extends Shape {
}
上述的子类本身就是抽象类,所以父类中的抽象方法可以不用自身实现,留给子类去实现。
另外,抽象的子类可以将父类的非抽象方法重写为抽象方法。
15.3 抽象类的作用
从语法上看,抽象类与抽象方法的语法并不复杂,主要使用 extends 关键字。
从语义上看,抽象类作为子类的模板,避免了子类设计的随意性。
从OOP的角度看,将类设计为抽象类,为系统提供多态实现,也会提高系统的可扩展性。在设计模式中和开源框架中都大量使用了抽象类。
15.4 小结
Java中的抽象类用于定义子类共享的通用模板,通过abstract修饰。抽象类可包含普通成员但不能实例化,抽象方法无方法体需子类实现。子类继承时,具体类必须实现所有抽象方法,抽象类可选择不实现。抽象类作为模板规范子类设计,提高系统可扩展性,广泛应用于设计模式和框架中。