JavaWeb 30 天入门:第六天 —— 抽象类与接口 - 指南
在前五天的学习中,我们掌握了 Java 基础语法、数组、方法以及面向对象的核心特性(封装、继承、多态)。今天我们将学习面向对象中的两个重要概念 ——抽象类和接口。它们是实现多态的重要载体,也是 JavaWeb 开发中框架设计的基础(如 Servlet 接口、Spring 中的各种接口)。理解抽象类和接口的区别与应用场景,能帮助我们写出更具扩展性的代码。
抽象类:不完整的类
什么是抽象类?
在面向对象中,有些类只作为父类被继承,而不会被实例化(创建对象),这类类称为抽象类。抽象类中可以包含普通方法和抽象方法(只有方法声明,没有方法实现)。
例如:"图形" 类(Shape)是一个抽象概念,它的 "计算面积" 方法无法具体实现(不同图形面积计算方式不同),因此可以定义为抽象类。
抽象类的定义
使用abstract
关键字定义抽象类和抽象方法:
// 抽象类
public abstract class 类名 {
// 普通属性
属性类型 属性名;
// 普通方法(有实现)
修饰符 返回值类型 方法名() {
// 方法体
}
// 抽象方法(没有实现,必须以分号结尾)
public abstract 返回值类型 抽象方法名(参数列表);
}
抽象类的特点
不能实例化:抽象类无法通过
new
关键字创建对象// 错误示例 Shape s = new Shape(); // 编译报错:Shape是抽象类,不能实例化
必须被继承:抽象类的子类必须重写所有抽象方法(除非子类也是抽象类)
可以包含普通成员:抽象类可以有普通属性、普通方法、构造方法(供子类调用)
抽象类实例:图形类体系
// 抽象父类:图形
public abstract class Shape {
// 普通属性
protected String color;
// 构造方法
public Shape(String color) {
this.color = color;
}
// 普通方法
public String getColor() {
return color;
}
// 抽象方法:计算面积(无法具体实现)
public abstract double calculateArea();
// 抽象方法:获取图形名称
public abstract String getShapeName();
}
// 子类:圆形(必须重写所有抽象方法)
public class Circle extends Shape {
private double radius; // 半径
public Circle(String color, double radius) {
super(color); // 调用父类构造
this.radius = radius;
}
// 重写抽象方法:计算面积
@Override
public double calculateArea() {
return Math.PI * radius * radius; // 圆面积公式:πr²
}
// 重写抽象方法:获取图形名称
@Override
public String getShapeName() {
return "圆形";
}
}
// 子类:矩形
public class Rectangle extends Shape {
private double length; // 长
private double width; // 宽
public Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}
@Override
public double calculateArea() {
return length * width; // 矩形面积公式:长×宽
}
@Override
public String getShapeName() {
return "矩形";
}
}
// 测试类
public class TestShape {
public static void main(String[] args) {
// 多态:抽象类引用指向子类对象
Shape circle = new Circle("红色", 5.0);
Shape rectangle = new Rectangle("蓝色", 4.0, 6.0);
// 调用抽象方法(实际执行子类实现)
System.out.println(circle.getShapeName() + "面积:" + circle.calculateArea());
System.out.println(rectangle.getShapeName() + "面积:" + rectangle.calculateArea());
}
}
运行结果:
圆形面积:78.53981633974483
矩形面积:24.0
抽象类的应用场景
- 抽取共性但无法具体实现的方法:当多个子类有共同方法但实现不同时,将方法声明为抽象方法
- 作为模板类:定义一套规范,强制子类实现特定方法(如 Java 中的
InputStream
抽象类) - 限制实例化:确保某些类只能作为父类使用(如框架中的基础类)
接口:行为规范
什么是接口?
接口(Interface)是一种完全抽象的类型,它只定义方法的声明(在 Java 8 + 中可以有默认方法和静态方法),不包含属性的实现(变量默认是public static final
)。
接口的本质是行为规范,它规定了一个类应该具有哪些方法,但不关心方法的具体实现。例如:"飞行" 接口可以规定fly()
方法,鸟、飞机等类都可以实现这个接口。
接口的定义与实现
使用interface
关键字定义接口,使用implements
关键字实现接口:
// 定义接口
public interface 接口名 {
// 常量(默认public static final)
数据类型 常量名 = 值;
// 抽象方法(默认public abstract)
返回值类型 方法名(参数列表);
// Java 8+:默认方法(有实现,用default修饰)
default 返回值类型 方法名() {
// 方法体
}
// Java 8+:静态方法(有实现)
static 返回值类型 方法名() {
// 方法体
}
}
// 实现接口
public class 类名 implements 接口名1, 接口名2 {
// 必须重写所有接口的抽象方法
@Override
public 返回值类型 方法名(参数列表) {
// 实现
}
}
接口的特点
- 不能实例化:接口无法创建对象
- 多实现:一个类可以实现多个接口(弥补 Java 单继承的不足)
- 接口继承接口:接口可以通过
extends
继承多个接口 - 方法默认修饰符:抽象方法默认
public abstract
,变量默认public static final
接口实例:行为规范
// 接口1:飞行行为
public interface Flyable {
// 抽象方法:飞行
void fly();
// 默认方法:起飞
default void takeOff() {
System.out.println("准备起飞...");
}
// 静态方法:获取飞行高度单位
static String getHeightUnit() {
return "米";
}
}
// 接口2:游泳行为
public interface Swimmable {
void swim();
}
// 类实现多个接口:鸭子(会飞也会游泳)
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("鸭子在低空飞行");
}
@Override
public void swim() {
System.out.println("鸭子在水面游弋");
}
}
// 类实现单个接口:鱼(只会游泳)
public class Fish implements Swimmable {
@Override
public void swim() {
System.out.println("鱼在水中游动");
}
}
// 测试类
public class TestInterface {
public static void main(String[] args) {
Duck duck = new Duck();
duck.takeOff(); // 调用接口的默认方法
duck.fly();
duck.swim();
Fish fish = new Fish();
fish.swim();
// 调用接口的静态方法
System.out.println("飞行高度单位:" + Flyable.getHeightUnit());
}
}
运行结果:
准备起飞...
鸭子在低空飞行
鸭子在水面游弋
鱼在水中游动
飞行高度单位:米
接口的应用场景
- 定义行为规范:如 Java 中的
Runnable
(线程运行)、Comparable
(比较)接口 - 实现多继承效果:一个类可以实现多个接口,拥有多种行为能力
- 解耦设计:接口作为标准,实现类可以灵活替换(如 JavaWeb 中的
Servlet
接口) - 回调机制:通过接口实现回调功能(如事件监听)
抽象类与接口的区别
特性 | 抽象类(Abstract Class) | 接口(Interface) |
---|---|---|
关键字 | abstract class | interface |
继承 / 实现方式 | 子类用extends 继承 | 类用implements 实现 |
继承数量 | 单继承(一个类只能继承一个抽象类) | 多实现(一个类可实现多个接口) |
方法实现 | 可以有普通方法(有实现)和抽象方法 | 抽象方法无实现;Java8 + 可有默认方法和静态方法 |
属性 | 可以有各种修饰符的属性 | 属性默认是public static final (常量) |
构造方法 | 有构造方法(供子类调用) | 没有构造方法 |
设计理念 | 体现 "is-a" 关系(继承体系) | 体现 "has-a" 关系(行为规范) |
如何选择抽象类和接口?
- 用抽象类:当需要定义一个类的基本结构(属性 + 方法),且部分方法可以实现时
- 用接口:当需要定义一组行为规范,且不关心具体实现时
- 两者结合:一个类可以继承抽象类的同时实现多个接口(如
ArrayList
继承AbstractList
并实现List
接口)
接口在 JavaWeb 中的典型应用
Servlet 接口:
// 所有Servlet都必须实现的接口 public interface Servlet { void init(ServletConfig config) throws ServletException; void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; void destroy(); // ...其他方法 }
过滤器接口(Filter):
public interface Filter { void init(FilterConfig filterConfig) throws ServletException; void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; void destroy(); }
这些接口定义了组件的基本行为规范,具体实现由开发者或框架提供,体现了接口的解耦优势。
总结与实践
知识点回顾
抽象类:
- 用
abstract
关键字定义,包含普通方法和抽象方法 - 不能实例化,必须被继承,子类需重写所有抽象方法
- 适合作为有共性但无法完全实现的父类
- 用
接口:
- 用
interface
关键字定义,主要包含抽象方法(Java8 + 支持默认方法和静态方法) - 类通过
implements
实现接口,可多实现 - 适合定义行为规范,实现多态和解耦
- 用
核心区别:抽象类体现继承关系,接口体现行为规范;抽象类单继承,接口多实现。
实践任务
创建支付系统接口:
- 定义
Payment
接口,包含抽象方法pay(double amount)
(支付)和refund(double amount)
(退款) - 定义
WechatPay
和Alipay
两个类,实现Payment
接口 - 在实现类中分别实现微信支付和支付宝支付的具体逻辑(打印支付 / 退款信息即可)
- 创建测试类,用多态方式调用支付和退款功能
- 定义
扩展任务:
- 创建
AbstractPayment
抽象类,实现Payment
接口,添加getPaymentName()
普通方法 - 让
WechatPay
和Alipay
继承AbstractPayment
,重写getPaymentName()
方法 - 体会抽象类和接口结合使用的优势
- 创建
思考:在实际开发中,为什么框架(如 Spring)大量使用接口而不是抽象类?