Appearance
第9课:抽象类与接口
🎯 学习目标
- 理解抽象类的概念和使用
- 掌握接口的定义和实现
- 理解抽象类与接口的区别
- 掌握接口的多实现
📖 一、抽象类
1. 抽象类的概念
抽象类:不能被实例化的类,用 abstract 修饰。
java
// 抽象类
public abstract class Animal {
String name;
// 普通方法
public void eat() {
System.out.println(name + " is eating");
}
// 抽象方法(没有方法体)
public abstract void makeSound();
}
// 子类必须实现所有抽象方法
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
// 使用
Animal animal = new Dog(); // ✅ 可以
// Animal animal = new Animal(); // ❌ 错误:不能实例化抽象类2. 抽象类的特点
- ❌ 不能被实例化
- ✅ 可以有构造方法(供子类调用)
- ✅ 可以有抽象方法和普通方法
- ✅ 可以有成员变量
- ✅ 子类必须实现所有抽象方法(除非子类也是抽象类)
java
public abstract class Shape {
String color;
// 构造方法
public Shape(String color) {
this.color = color;
}
// 抽象方法
public abstract double area();
public abstract double perimeter();
// 普通方法
public void display() {
System.out.println("Color: " + color);
}
}
public class Circle extends Shape {
double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
}📖 二、接口
1. 接口的定义
java
// 接口
public interface Flyable {
// 抽象方法(默认 public abstract)
void fly();
// 常量(默认 public static final)
int MAX_SPEED = 1000;
}
// 实现接口
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird is flying");
}
}2. 接口的特点
- ✅ 所有方法默认是
public abstract - ✅ 所有变量默认是
public static final(常量) - ✅ 不能有构造方法
- ✅ 支持多实现
- ✅ JDK 8+ 可以有默认方法和静态方法
java
public interface MyInterface {
// 抽象方法
void method1();
// 默认方法(JDK 8+)
default void method2() {
System.out.println("Default method");
}
// 静态方法(JDK 8+)
static void method3() {
System.out.println("Static method");
}
// 私有方法(JDK 9+)
private void method4() {
System.out.println("Private method");
}
}3. 多实现
java
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
// 一个类可以实现多个接口
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Duck flying");
}
@Override
public void swim() {
System.out.println("Duck swimming");
}
}📖 三、抽象类 vs 接口
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class | interface |
| 继承/实现 | extends(单继承) | implements(多实现) |
| 构造方法 | ✅ 可以有 | ❌ 不能有 |
| 成员变量 | ✅ 可以有 | ❌ 只能有常量 |
| 方法 | 抽象 + 普通 | 抽象(默认) + 默认 + 静态 |
| 访问修饰符 | 任意 | public(默认) |
何时使用?
使用抽象类:
- IS-A 关系(猫是动物)
- 需要共享代码
- 需要成员变量
使用接口:
- CAN-DO 关系(鸟能飞)
- 需要多继承行为
- 定义规范
java
// ✅ 抽象类:IS-A 关系
abstract class Animal {
String name;
public abstract void eat();
}
// ✅ 接口:CAN-DO 关系
interface Flyable {
void fly();
}
class Bird extends Animal implements Flyable {
public void eat() { }
public void fly() { }
}📖 四、接口的继承
java
// 接口可以继承接口(可以多继承)
interface A {
void methodA();
}
interface B {
void methodB();
}
interface C extends A, B {
void methodC();
}
// 实现 C 需要实现 A、B、C 的所有方法
class MyClass implements C {
public void methodA() { }
public void methodB() { }
public void methodC() { }
}📖 五、默认方法和静态方法
1. 默认方法(JDK 8+)
java
public interface MyInterface {
// 默认方法
default void log(String message) {
System.out.println("Log: " + message);
}
}
public class MyClass implements MyInterface {
// 可以不重写默认方法
}
// 使用
MyClass obj = new MyClass();
obj.log("Hello"); // Log: Hello2. 静态方法(JDK 8+)
java
public interface MyInterface {
static void staticMethod() {
System.out.println("Static method");
}
}
// 使用
MyInterface.staticMethod();3. 默认方法冲突
java
interface A {
default void method() {
System.out.println("A");
}
}
interface B {
default void method() {
System.out.println("B");
}
}
// 冲突:必须重写
class MyClass implements A, B {
@Override
public void method() {
A.super.method(); // 调用 A 的默认方法
// 或
B.super.method(); // 调用 B 的默认方法
}
}📖 六、实战应用
1. 定义规范
java
// USB 接口规范
public interface USB {
void connect();
void disconnect();
void transferData();
}
// U盘
public class UDisk implements USB {
public void connect() {
System.out.println("U盘已连接");
}
public void disconnect() {
System.out.println("U盘已断开");
}
public void transferData() {
System.out.println("传输数据");
}
}
// 鼠标
public class Mouse implements USB {
public void connect() {
System.out.println("鼠标已连接");
}
public void disconnect() {
System.out.println("鼠标已断开");
}
public void transferData() {
System.out.println("发送鼠标数据");
}
}2. 策略模式
java
// 支付接口
public interface PaymentStrategy {
void pay(double amount);
}
// 微信支付
public class WeChatPay implements PaymentStrategy {
public void pay(double amount) {
System.out.println("微信支付: " + amount);
}
}
// 支付宝支付
public class AliPay implements PaymentStrategy {
public void pay(double amount) {
System.out.println("支付宝支付: " + amount);
}
}
// 使用
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(double amount) {
paymentStrategy.pay(amount);
}
}⚠️ 常见陷阱
陷阱1:接口设计过大
接口方法太多会迫使实现类依赖不需要的能力。接口应小而聚焦。
陷阱2:抽象类承担过多职责
抽象类如果包含大量状态和流程,很容易变成难维护的父类模板。
陷阱3:把接口当常量池
用接口只放常量是反模式,会污染实现类的 API。常量应放在专门的 final 工具类或枚举中。
陷阱4:默认方法冲突
多个接口提供同名默认方法时,实现类必须显式解决冲突。
🆚 Java vs C 对比
| 特性 | C | Java |
|---|---|---|
| 抽象能力 | 头文件 + 函数指针约定 | abstract class / interface |
| 多实现 | 手动组合函数指针表 | 一个类可实现多个接口 |
| 默认实现 | 手写公共函数 | 接口 default 方法 |
| 状态复用 | struct 嵌套 | 抽象类字段和方法 |
对 C 程序员来说,接口可以理解为一组必须实现的函数签名;抽象类则更像带部分默认实现和状态的基类。
💡 最佳实践
- 接口命名:动词 + able(Runnable、Comparable、Serializable)
- 接口方法数量:尽量少(单一职责)
- 优先使用接口:比抽象类更灵活
- 面向接口编程:降低耦合度
📝 练习
完成 练习/Ex09_Abstract_Interface.java:
- 抽象类实践
- 接口实现
- 多实现
- 默认方法
- 抽象类 vs 接口
- 综合:支付系统
🎓 下一步
- 第10课:内部类 - 成员内部类、局部内部类、匿名内部类、静态内部类