引言:为什么需要抽象?
在面向对象设计中,我们经常需要定义一些"只给出方法声明,不给出具体实现"的类。这就是抽象的概念。
抽象类比
抽象就像建筑图纸:
图纸(抽象类/接口):
- 规定房子必须有"开门方法"、"关门方法"
- 但不规定具体怎么开门、怎么关门
具体建筑(实现类):
- 必须按照图纸实现开门、关门
- 但具体怎么开门、关门由建筑商决定
抽象类
什么是抽象类?
抽象类是用abstract关键字修饰的类,不能直接实例化,只能被继承。
抽象方法
抽象方法是没有方法体的方法,子类必须实现:
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
public abstract double getArea();
public abstract double getPerimeter();
public void printColor() {
System.out.println("颜色: " + color);
}
}
抽象类的特点
使用
abstract关键字修饰不能直接实例化(不能new)
可以包含抽象方法和具体方法
子类继承抽象类后,必须实现所有抽象方法
可以有构造方法(供子类调用)
可以包含成员变量
抽象类示例
abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public abstract void sound();
public abstract void eat();
public void sleep() {
System.out.println(name + "正在睡觉");
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void sound() {
System.out.println(name + "汪汪汪");
}
@Override
public void eat() {
System.out.println(name + "正在吃狗粮");
}
}
接口
什么是接口?
接口是一种特殊的抽象类型,完全抽象,只声明方法,不提供实现(Java 8之前)。
接口定义
public interface Drawable {
int MAX_SIZE = 100;
void draw();
void move(int x, int y);
}
接口实现
public class Circle implements Drawable {
private int x, y;
private double radius;
public Circle(int x, int y, double radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public void draw() {
System.out.println("画一个圆,圆心(" + x + "," + y + "),半径" + radius);
}
@Override
public void move(int x, int y) {
this.x = x;
this.y = y;
System.out.println("移动到(" + x + "," + y + ")");
}
}
接口的特性
接口完全抽象,所有方法都是抽象的(Java 8之前)
接口变量自动public static final(常量)
接口方法自动public abstract
类实现接口使用implements关键字
一个类可以实现多个接口
接口不能实例化
接口没有构造方法
Java 8的接口新特性
默认方法(Default Methods)
接口可以包含带默认实现的方法:
public interface Drawable {
void draw();
default void print() {
System.out.println("打印图形");
}
static void help() {
System.out.println("这是一个Drawable接口");
}
}
静态方法(Static Methods)
public interface MathUtil {
static int add(int a, int b) {
return a + b;
}
static int multiply(int a, int b) {
return a * b;
}
}
int result = MathUtil.add(3, 5); // 调用静态方法
抽象类 vs 接口
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 继承 | 单继承(只能extends一个) | 多实现(可以implements多个) |
| 属性 | 任意类型 | 只能是常量(public static final) |
| 方法 | 抽象方法 + 具体方法 | 抽象方法 + 默认方法 + 静态方法 |
| 构造方法 | 可以有 | 不能有 |
| 成员变量 | 可以有 | 只能是常量 |
| 使用场景 | "是什么"(is-a) | "能做什么"(can-do) |
何时使用抽象类?
- 需要共享代码和状态(成员变量)
- 类之间有"是"的关系(Dog is Animal)
- 需要非public的成员
何时使用接口?
- 需要定义行为规范
- 类之间有"能"的关系(Bird can Fly)
- 需要多继承的场景
多接口实现
一个类可以实现多个接口:
interface Walkable {
void walk();
}
interface Runnable {
void run();
}
interface Swimmable {
void swim();
}
class Duck implements Walkable, Runnable, Swimmable {
@Override
public void walk() {
System.out.println("鸭子走路");
}
@Override
public void run() {
System.out.println("鸭子跑步");
}
@Override
public void swim() {
System.out.println("鸭子游泳");
}
}
接口继承
接口可以继承其他接口:
interface Animal {
void eat();
}
interface Pet extends Animal {
void play();
}
class Dog implements Pet {
@Override
public void eat() {
System.out.println("狗吃东西");
}
@Override
public void play() {
System.out.println("狗玩耍");
}
}
实战练习
练习:设计文件系统
interface FileSystemItem {
String getName();
long getSize();
default void print() {
System.out.println(getName() + " (" + getSize() + " bytes)");
}
}
class File implements FileSystemItem {
private String name;
private long size;
public File(String name, long size) {
this.name = name;
this.size = size;
}
@Override
public String getName() { return name; }
@Override
public long getSize() { return size; }
}
class Folder implements FileSystemItem {
private String name;
private List items = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void addItem(FileSystemItem item) {
items.add(item);
}
@Override
public String getName() { return name; }
@Override
public long getSize() {
return items.stream().mapToLong(FileSystemItem::getSize).sum();
}
@Override
public void print() {
System.out.println("文件夹: " + name);
for (FileSystemItem item : items) {
item.print();
}
}
}
小结
- 抽象类:用abstract修饰,不能实例化,需继承
- 接口:完全抽象,多个接口用逗号分隔实现
- 抽象类单继承,接口多实现
- 抽象类用于"是"的关系,接口用于"能"的关系
- 接口默认方法(Java 8)提供带实现的可选方法
- Java 8+接口可以包含默认方法、静态方法
☕ Java 在线代码编辑器
📝 运行结果