抽象类(abstract class)
抽象类是 Java 中一种特殊的类,用 abstract 关键字修饰,它不能被实例化,主要用于作为其他类的父类,提取 子类的共性并定义规范。
抽象类的核心特性
- 不能实例化
抽象类无法直接创建对象(new AbstractClass()
会编译报错),必须通过子类继承并实现所有抽象方法后,才能实例化子类对象。
java
abstract class Shape { ... }
// Shape shape = new Shape(); // 错误:抽象类不能实例化
- 可包含抽象方法和普通方法
- 抽象方法:用
abstract
修饰,只有方法声明(无方法体),必须由子类强制重写。
语法:abstract 返回值类型 方法名(参数列表);
- 普通方法:有完整的方法体(实现逻辑),子类可直接继承使用,也可选择重写。
- 可包含成员变量和构造方法
- 成员变量:可使用任意访问修饰符(
private
/protected
/public
等),子类可通过继承或getter
访问。 - 构造方法:抽象类有构造方法(用于子类初始化时调用),但不能直接用于实例化自身。
抽象类的定义与使用示例
java
public class AbstractDemo {
public static void main(String[] args) {
// 通过子类实例化(抽象类本身不能实例化)
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
System.out.println("圆的面积:" + circle.calculateArea()); // 78.5
System.out.println("矩形的面积:" + rectangle.calculateArea()); // 24
circle.display(); // 输出:这是一个图形
}
}
// 抽象类:定义图形的共性
abstract class Shape {
// 成员变量
protected String color;
// 构造方法(供子类调用)
public Shape() {
this.color = "白色";
}
// 抽象方法(必须由子类实现):计算面积
public abstract double calculateArea();
// 普通方法(有实现):显示图形信息
public void display() {
System.out.println("这是一个图形,颜色:" + color);
}
}
// 子类1:圆形(继承抽象类并实现抽象方法)
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
// 实现抽象方法:计算圆面积
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// 子类2:矩形(继承抽象类并实现抽象方法)
class Rectangle extends Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
// 实现抽象方法:计算矩形面积
@Override
public double calculateArea() {
return length * width;
}
}
抽象类的作用
- 提取共性,实现代码复用
将多个子类的共同属性(如 color
)和方法(如 display()
)定义在抽象类中,子类无需重复编写。
- 定义规范,强制子类实现
抽象方法(如 calculateArea()
)规定了子类必须具备的功能,但不限制具体实现(圆和矩形的面积计算方式不同),既保证了一致性,又保留了灵活性。
- 作为多态的载体
抽象类的引用可以指向任意子类对象(向上转型),实现 "同一接口,不同实现" 的多态效果(如示例中用 Shape
引用分别指向 Circle
和 Rectangle
)。
使用注意事项
- 抽象类中可以没有抽象方法(全是普通方法),但此时仍不能实例化,通常用于禁止直接创建对象。
- 子类继承抽象类后,必须重写所有抽象方法,否则子类也必须声明为抽象类(层层传递,直到某个子类完全实现)。
- 抽象类不能用
final
修饰(final
类不能被继承,与抽象类的设计目的冲突)。
接口
接口(interface
)是 Java 中定义行为规范的重要机制,它专注于描述 "能做什么 ",而不关心 "如何做"。
接口的核心组成(Java 8+)
接口可以包含常量 、抽象方法 、默认方法 和静态方法,具体规则如下:
常量定义
接口中的变量默认被 public
static
final
修饰(可省略),必须初始化且不可修改,本质是全局常量。
语法:[public static final] 数据类型 常量名 = 值;
示例:
java
interface MathConstants {
double PI = 3.1415926; // 等价于 public static final double PI = ...
int MAX_VALUE = 1000; // 全局常量
}
抽象方法(核心)
接口中的抽象方法默认被 public
abstract
修饰(可省略),只有方法声明,无方法体,必须由实现类实现。
语法:[public abstract] 返回值类型 方法名(参数列表);
示例:
java
interface Flyable {
void fly(); // 等价于 public abstract void fly();
void land(); // 抽象方法,需实现类重写
}
默认方法(Java 8 新增)
用 default
修饰,有方法体,供实现类直接使用或重写,解决接口升级时的兼容性问题(无需修改所有实现类)。
语法:public default 返回值类型 方法名(参数列表) { ... }
示例:
java
interface Flyable {
void fly();
// 默认方法(有实现)
default void prepare() {
System.out.println("准备飞行");
}
}
// 实现类可直接使用默认方法
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟飞行");
}
// 未重写prepare(),使用接口默认实现
}
// 实现类也可重写默认方法
class Plane implements Flyable {
@Override
public void fly() {
System.out.println("飞机飞行");
}
@Override
public void prepare() {
System.out.println("飞机检查设备,准备飞行"); // 重写默认方法
}
}
静态方法(Java 8 新增)
用 static
修饰,有方法体,属于接口本身,只能通过接口名调用,实现类不能重写。
语法:public static 返回值类型 方法名(参数列表) { ... }
示例:
java
interface Flyable {
void fly();
// 静态方法
static void showInfo() {
System.out.println("这是飞行接口");
}
}
// 调用静态方法
public class Test {
public static void main(String[] args) {
Flyable.showInfo(); // 正确:通过接口名调用
// Bird.showInfo(); // 错误:实现类不能调用接口的静态方法
}
}
接口的使用规则
- 实现接口 :类通过
implements
关键字实现接口,需重写所有抽象方法(默认方法和静态方法可选重写 / 使用)。
java
class Bird implements Flyable {
@Override
public void fly() { ... } // 必须重写抽象方法
}
- 多实现:一个类可以实现多个接口(用逗号分隔),弥补 Java 单继承的限制。
java
interface Flyable { void fly(); }
interface Swimmable { void swim(); }
// 同时实现两个接口
class Duck implements Flyable, Swimmable {
@Override public void fly() { ... }
@Override public void swim() { ... }
}
- 接口继承接口:接口可以通过 extends 继承其他接口(支持多继承),子接口包含父接口的所有成员。
java
interface Moveable { void move(); }
interface Flyable extends Moveable { void fly(); } // 继承Moveable
// 实现子接口需重写所有抽象方法(包括父接口的)
class Plane implements Flyable {
@Override public void move() { ... }
@Override public void fly() { ... }
}
- 不能实例化:接口无法直接创建对象,必须通过实现类实例化,且接口引用可指向实现类对象(多态)。
java
Flyable flyable = new Bird(); // 接口引用指向实现类对象(多态)
接口的典型应用场景
- 定义规范 :作为系统模块或组件的 "契约",明确必须实现的功能(如
Runnable
接口规定线程任务)。 - 多态基础 :通过接口引用实现 "同一接口,不同实现"(如
List
接口有ArrayList
、LinkedList
等实现)。 - 功能扩展 :为类添加额外能力(无继承关系的类可实现同一接口,如
Bird
和Plane
都实现Flyable
)。
区别与应用场景
核心区别(语法与特性)
对比维度 | 抽象类( **abstract class** ) |
接口( **interface** ) |
---|---|---|
定义关键字 | abstract class |
interface |
继承 / 实现方式 | 子类用 extends 继承(单继承,一个类只能继承一个抽象类) |
类用 implements 实现(多实现,一个类可实现多个接口) |
成员变量 | 可包含任意修饰符的变量(private /protected /public 等,可修改) |
只能是 public static final 常量(必须初始化,不可修改) |
方法类型 | + 抽象方法(abstract ,无实现) + 普通方法(有实现) + 静态方法(static ) |
+ 抽象方法(默认 public abstract ) + 默认方法(default ,有实现) + 静态方法(static ,有实现) |
构造方法 | 有构造方法(供子类初始化时调用) | 无构造方法 |
实例化 | 不能直接实例化,需子类实现所有抽象方法后实例化子类 | 不能直接实例化,需实现类实现所有抽象方法后实例化实现类 |
设计意图 | 表示 "is-a " 关系(强调继承,描述 "是什么") |
表示 "has-a " 关系(强调功能,描述 "能做什么") |
应用场景选择
- 优先使用抽象类的场景
- 提取子类共性属性和方法 :当多个子类存在共同的属性(如
name
、age
)和已实现的方法(如eat()
)时,抽象类可作为 "模板" 统一管理这些共性,避免重复代码。
示例:
定义 Animal
抽象类,包含所有动物共有的 name
属性和 sleep()
方法,子类(Dog
、Cat
)只需实现各自的 makeSound()
抽象方法。
- 类之间存在明确的 "
**is-a**
" 继承关系:当子类是父类的 "一种具体类型" 时,适合用抽象类。
示例:Student is a Person
、Car is a Vehicle
,此时 Person
和 Vehicle
适合作为抽象类。
- 需要定义非抽象方法:当多个子类需要复用相同的方法实现(而非重复编写)时,抽象类的普通方法可以满足这一需求。
- 优先使用接口的场景
- 定义跨类别的功能规范:当不同类(无继承关系)需要具备相同的行为时,接口可作为 "功能契约"。
示例:Bird
、Plane
、Insect
无继承关系,但都能 "飞行",可通过 Flyable
接口定义 fly()
方法。
- 需要多实现能力:Java 不支持类的多继承,但一个类可实现多个接口,从而拥有多种功能。
示例:Duck
可同时实现 Swimmable
(游泳)和 Flyable
(飞行)接口,表明它 "既能游又能飞"。
- 框架或 API 的规范定义:接口常用于定义模块间的交互标准(如回调接口、监听器),实现 "依赖倒置"(依赖抽象而非具体实现)。
示例:Runnable
接口定义线程任务的规范,List
接口定义集合的通用操作。
- 接口升级兼容 :Java 8+ 的默认方法(
default
)允许在接口中添加新方法而不影响现有实现类,适合接口的迭代升级。