抽象类的定义
-
使用
abstract
关键字修饰类:javaabstract class AbstractPlayer {}
-
按规范,抽象类命名应以 Abstract 或 Base 开头(如:
AbstractPlayer
、BaseService
)。
抽象类的特征
-
不能被实例化:
javaAbstractPlayer player = new AbstractPlayer(); // ❌ 编译错误
-
可以被继承 :子类使用
extends
继承抽象类javapublic class BasketballPlayer extends AbstractPlayer {}
-
包含抽象方法:
抽象方法必须在抽象类中定义
抽象方法没有方法体,只定义方法签名
子类必须实现父类中的所有抽象方法,否则子类也必须是抽象类
javaabstract void play();
-
可以包含普通方法:抽象类可以混合抽象与非抽象方法
javapublic abstract class AbstractPlayer { abstract void play(); public void sleep() { System.out.println("运动员也要休息"); } }
抽象类的应用场景
1️⃣ 代码复用场景
- 抽象类可以定义多个子类共有的功能 (如
sleep()
方法),避免重复代码。 - 子类继承后可以直接使用这些公共方法。
2️⃣ 统一接口、延迟实现场景
-
抽象类可作为一种模板或框架,规定子类必须实现的功能。
-
例如:
play()
方法定义了所有运动员都要"从事某项运动"的行为,但不同子类各自实现不同内容。javaabstract class AbstractPlayer { abstract void play(); }
抽象类的进阶示例:文件读取器
基类定义(模板模式思想)
java
abstract class BaseFileReader {
protected Path filePath;
protected BaseFileReader(Path filePath) {
this.filePath = filePath;
}
public List<String> readFile() throws IOException {
return Files.lines(filePath)
.map(this::mapFileLine)
.collect(Collectors.toList());
}
protected abstract String mapFileLine(String line);
}
readFile()
定义了文件读取的整体流程。mapFileLine()
是一个钩子方法(由子类实现不同转换逻辑)。
子类实现不同的读取方式
java
class LowercaseFileReader extends BaseFileReader {
@Override
protected String mapFileLine(String line) {
return line.toLowerCase();
}
}
class UppercaseFileReader extends BaseFileReader {
@Override
protected String mapFileLine(String line) {
return line.toUpperCase();
}
}
测试类
java
public class FileReaderTest {
public static void main(String[] args) throws Exception {
URL location = FileReaderTest.class.getClassLoader().getResource("helloworld.txt");
Path path = Paths.get(location.toURI());
BaseFileReader lower = new LowercaseFileReader(path);
BaseFileReader upper = new UppercaseFileReader(path);
System.out.println(lower.readFile());
System.out.println(upper.readFile());
}
}
抽象类的总结
特征 | 说明 |
---|---|
实例化 | 不可直接实例化 |
方法类型 | 可包含抽象方法与普通方法 |
子类要求 | 必须实现所有抽象方法或自身声明为抽象类 |
用途 | 代码复用、定义模板、约束子类行为 |
核心思想
抽象类是"模板"与"约束"的结合。
它既能封装通用逻辑(复用代码),又能通过抽象方法约束子类实现特定行为。
接口的定义与语法
-
使用
interface
关键字定义接口:javapublic interface Electronic { String workconsistent = "workconsistent"; // 常量 int getElectricityUse(); // 抽象方法 static boolean isEnergyEfficient(String type) { ... } // 静态方法 (Java 8+) default void printDescription() { ... } // 默认方法 (Java 8+) }
接口成员特征
成员类型 | 默认修饰符 | 说明 |
---|---|---|
变量 | public static final |
即常量,值不可修改 |
抽象方法 | public abstract |
无方法体,需由实现类实现 |
静态方法 | public static |
需用接口名调用,不可由实例调用 |
默认方法 | public default |
可有方法体,供实现类直接复用 |
接口可包含静态方法与默认方法,用以增强接口的扩展性和兼容性。
接口的限制与特性
-
接口 不能被实例化 ,必须由类通过
implements
实现:javapublic class Computer implements Electronic { ... }
-
接口 不能使用
final
修饰(否则无法被实现)。 -
抽象方法 不能是
private
、protected
或final
。 -
接口变量是常量(
public static final
),值不可修改。 -
接口可以是空接口(标记接口),如:
javapublic interface Serializable {}
实现该接口的类具有某种"标识性"功能(例如可被序列化)。
接口的作用
1️⃣ 提供功能约定
实现接口的类具有特定功能,例如:
Cloneable
→ 支持克隆Serializable
→ 支持序列化Comparable
/Comparator
→ 支持比较
示例:Cloneable 接口
java
public class CloneableTest implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2️⃣ 支持多重继承
Java 不支持类的多继承,但接口可以实现"多继承行为":
java
public interface Fly { void fly(); }
public interface Run { void run(); }
public class Pig implements Fly, Run {
public void fly() { System.out.println("会飞的猪"); }
public void run() { System.out.println("会跑的猪"); }
}
一个类可实现多个接口,避免了"菱形继承问题"。
3️⃣ 实现多态(Polymorphism)
通过接口引用不同实现类,实现同一方法不同表现:
java
interface Shape { String name(); }
class Circle implements Shape {
public String name() { return "圆"; }
}
class Square implements Shape {
public String name() { return "正方形"; }
}
List<Shape> shapes = List.of(new Circle(), new Square());
for (Shape s : shapes){
// 圆、正方形
System.out.println(s.name());
}
多态的三个前提:
- 有继承或实现关系;
- 子类重写父类或接口方法;
- 父类引用指向子类对象。
接口的三种典型设计模式
策略模式(Strategy Pattern)
- 定义一组算法(策略)并封装,调用方通过接口使用不同实现。
java
// 策略模式示例:多种支付方式
// 1. 定义支付策略接口
interface PaymentStrategy {
void pay(double amount);
}
// 2. 定义不同的支付策略实现
class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付 " + amount + " 元");
}
}
class WeChatPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用微信支付 " + amount + " 元");
}
}
class CreditCardStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用信用卡支付 " + amount + " 元");
}
}
// 3. 支付上下文类,负责调用具体策略
class PaymentContext {
private PaymentStrategy paymentStrategy;
public PaymentContext(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void executePayment(double amount) {
paymentStrategy.pay(amount);
}
}
// 4. 测试类(主程序)
public class StrategyDemo {
public static void main(String[] args) {
PaymentContext alipayContext = new PaymentContext(new AlipayStrategy());
PaymentContext wechatContext = new PaymentContext(new WeChatPayStrategy());
PaymentContext creditContext = new PaymentContext(new CreditCardStrategy());
alipayContext.executePayment(199.99);
wechatContext.executePayment(88.88);
creditContext.executePayment(500.00);
}
}
适配器模式(Adapter Pattern)
- 用抽象类实现接口并置空方法,新类继承该抽象类,只需覆盖必要方法。
java
// 定义播放器接口
interface MediaPlayer {
void playMp3(String fileName);
void playMp4(String fileName);
void playFlac(String fileName);
}
// 抽象适配器类:实现接口但提供空方法(防止子类必须实现所有方法)
abstract class MediaAdapter implements MediaPlayer {
public void playMp3(String fileName) {}
public void playMp4(String fileName) {}
public void playFlac(String fileName) {}
}
// 子类根据需要只实现自己关心的方法
class Mp3Player extends MediaAdapter {
@Override
public void playMp3(String fileName) {
System.out.println(" 正在播放 MP3 文件:" + fileName);
}
}
class Mp4Player extends MediaAdapter {
@Override
public void playMp4(String fileName) {
System.out.println(" 正在播放 MP4 文件:" + fileName);
}
}
class FlacPlayer extends MediaAdapter {
@Override
public void playFlac(String fileName) {
System.out.println(" 正在播放 FLAC 文件:" + fileName);
}
}
// 测试类
public class AdapterDemo {
public static void main(String[] args) {
MediaPlayer mp3 = new Mp3Player();
MediaPlayer mp4 = new Mp4Player();
MediaPlayer flac = new FlacPlayer();
mp3.playMp3("周杰伦 - 晴天.mp3");
mp4.playMp4("电影 - 功夫.mp4");
flac.playFlac("林俊杰 - 江南.flac");
}
}
工厂模式(Factory Pattern)
- 定义接口规范产品与工厂,通过不同工厂创建不同对象。
java
// 图形接口
interface Shape {
void draw();
}
// 具体图形类:圆形、矩形、三角形
class Circle implements Shape {
@Override
public void draw() {
System.out.println("正在画一个圆形 🟢");
}
}
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("正在画一个矩形 🟥");
}
}
class Triangle implements Shape {
@Override
public void draw() {
System.out.println("正在画一个三角形 🔺");
}
}
// 抽象工厂接口
interface ShapeFactory {
Shape createShape();
}
// 具体工厂类
class CircleFactory implements ShapeFactory {
@Override
public Shape createShape() {
return new Circle();
}
}
class RectangleFactory implements ShapeFactory {
@Override
public Shape createShape() {
return new Rectangle();
}
}
class TriangleFactory implements ShapeFactory {
@Override
public Shape createShape() {
return new Triangle();
}
}
// 测试类
public class FactoryDemo {
// 静态方法,接收不同的工厂对象
public static void drawShape(ShapeFactory factory) {
Shape shape = factory.createShape();
shape.draw();
}
public static void main(String[] args) {
drawShape(new CircleFactory());
drawShape(new RectangleFactory());
drawShape(new TriangleFactory());
}
}
抽象类 vs 接口
对比点 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract class |
interface |
是否可包含实现 | 可包含具体方法 | 仅方法声明 |
成员变量 | 任意类型 | public static final 常量 |
静态代码块 | 可以 | 不允许 |
继承 / 实现数量 | 单继承 | 多实现 |
设计目的 | 对类的抽象(模板式设计) | 对行为的抽象(规范式设计) |
关系表达 | "是一个(is-a)" | "具有......能力(has-a)" |
修改影响 | 改抽象类 → 子类不一定改 | 改接口 → 所有实现类需同步改动 |
接口是行为规范,抽象类是模板设计。
- 接口 关注"能力",强调"能做什么";
- 抽象类 关注"共性",强调"是什么";
- 抽象类提供"部分实现",接口提供"统一约定";
- 接口支持多继承,抽象类支持代码复用。
内部类的四种类型
成员内部类
定义在外部类的成员位置 ,没有使用 static
修饰。
java
public class Icheng {
private String hobby = "写代码";
static int age = 25;
class Friend {
public void introduce() {
System.out.println("一成的爱好是:" + hobby);
System.out.println("一成今年:" + age + " 岁");
}
}
public static void main(String[] args) {
Icheng icheng = new Icheng();
Icheng.Friend friend = icheng.new Friend();
friend.introduce();
}
}
要点:
-
Friend
是成员内部类。 -
可以访问外部类的:
- 私有成员变量
hobby
; - 静态成员变量
age
。
- 私有成员变量
-
创建方式:
外部类对象.new 内部类()
。
局部内部类
java
public class IchengCafe {
private String cafeName = "一成的下午茶屋";
public void closeCafe() {
// 局部内部类:用于记录当天的结算信息
class DailyReport {
private int customers = 42;
private double income = 589.5;
public void printReport() {
System.out.println(" 店铺:" + cafeName);
System.out.println(" 今日接待顾客数:" + customers);
System.out.println(" 今日营业额:" + income + " 元");
}
}
// 在方法内创建并使用局部内部类对象
DailyReport report = new DailyReport();
report.printReport();
}
public static void main(String[] args) {
IchengCafe cafe = new IchengCafe();
cafe.closeCafe();
// 店铺:一成的下午茶屋
// 今日接待顾客数:42
// 今日营业额:589.5 元
}
}
说明:
DailyReport
定义在closeCafe()
方法内部,只能在此方法中使用。- 生命周期与
closeCafe()
方法一致,方法执行完就销毁。 - 不能使用
public
、private
、protected
或static
修饰。 - 常用于在方法中临时封装逻辑或实现接口。
3️⃣ 匿名内部类
没有名字的内部类 ,常用于简化
一次性类的使用(尤其是回调和线程)。
java
public class ThreadDemo {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
t.start();
}
}
特征与要点:
- 无类名、无构造方法。
- 直接通过
new 接口/父类()
创建对象并重写方法。 - 常用于:事件监听、线程、回调等。
- 本质上是一个语法糖,用于快速定义临时子类或实现类。
4️⃣ 静态内部类
java
public class Icheng {
static int age = 21;
double money = 99.9;
static class Friend {
public Friend() {
// 21
System.out.println(age); // ✅ 可以访问外部类静态成员
// System.out.println(money); ❌ 非静态成员不能访问
}
}
public static void main(String[] args) {
new Friend(); // 直接创建静态内部类对象
}
}
说明:
Friend
是 静态内部类 ,与外部类Icheng
没有实例绑定关系。- 可以直接访问外部类的 静态变量
age
,
但不能访问 非静态变量money
。 - 创建方式简洁:
new Icheng.Friend()
或在同类中直接new Friend()
。
使用内部类的原因与优势
💬 引用《Think in Java》:
"使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现。"
内部类的优势
- 实现多重继承效果
内部类可以在外部类继承某个类的同时,实现另一个接口或类的功能。 - 实例独立性
每个内部类对象都可拥有自己的状态,与外部类实例独立。 - 多样实现同一接口
在一个外部类中,可以有多个内部类分别以不同方式实现同一个接口。 - 延迟创建
内部类对象可在任意时刻创建,不依赖外部类的初始化。 - 更好的封装性
内部类可隐藏实现细节,对外部完全不可见。
对比表
类型 | 定义位置 | 是否静态 | 是否依附外部类对象 | 可访问外部类成员 | 修饰符限制 | 使用场景 |
---|---|---|---|---|---|---|
成员内部类 | 外部类成员处 | 否 | 是 | 所有成员 | 无限制 | 外部类的辅助逻辑 |
局部内部类 | 方法/作用域内 | 否 | 是 | 可访问局部变量(final 或 effectively final) | 不能用 public/private/protected/static | 方法内临时使用 |
匿名内部类 | 表达式内 | 否 | 是 | 可访问外部成员 | 无修饰符 | 简化接口或抽象类实现 |
静态内部类 | 外部类成员处 | 是 | 否 | 仅可访问静态成员 | 可用 public/private/protected | 辅助类、工具类、Builder 模式 |
核心思想
内部类 = 一种在类中再定义类的机制,强化封装、灵活扩展。
- 成员内部类 → 外部类的"成员"
- 局部内部类 → 方法的"局部变量"
- 匿名内部类 → 一次性"快捷实现类"
- 静态内部类 → 独立于外部类存在的"嵌套类"