JavaSE面向对象(下)
抽象类的定义
-
使用
abstract
关键字修饰类:abstract class AbstractPlayer {}
-
按规范,抽象类命名应以 Abstract 或 Base 开头(如:
AbstractPlayer
、BaseService
)。
抽象类的特征
-
不能被实例化:
AbstractPlayer player = new AbstractPlayer(); // ❌ 编译错误
-
可以被继承:子类使用
extends
继承抽象类public class BasketballPlayer extends AbstractPlayer {}
-
包含抽象方法:
抽象方法必须在抽象类中定义
抽象方法没有方法体,只定义方法签名
子类必须实现父类中的所有抽象方法,否则子类也必须是抽象类abstract void play();
-
可以包含普通方法:抽象类可以混合抽象与非抽象方法
public abstract class AbstractPlayer {abstract void play();public void sleep() {System.out.println("运动员也要休息");} }
抽象类的应用场景
1️⃣ 代码复用场景
- 抽象类可以定义多个子类共有的功能(如
sleep()
方法),避免重复代码。 - 子类继承后可以直接使用这些公共方法。
2️⃣ 统一接口、延迟实现场景
-
抽象类可作为一种模板或框架,规定子类必须实现的功能。
-
例如:
play()
方法定义了所有运动员都要“从事某项运动”的行为,但不同子类各自实现不同内容。abstract class AbstractPlayer {abstract void play(); }
抽象类的进阶示例:文件读取器
基类定义(模板模式思想)
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()
是一个钩子方法(由子类实现不同转换逻辑)。
子类实现不同的读取方式
class LowercaseFileReader extends BaseFileReader {@Overrideprotected String mapFileLine(String line) {return line.toLowerCase();}
}class UppercaseFileReader extends BaseFileReader {@Overrideprotected String mapFileLine(String line) {return line.toUpperCase();}
}
测试类
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
关键字定义接口:public 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
实现:public class Computer implements Electronic { ... }
-
接口 不能使用
final
修饰(否则无法被实现)。 -
抽象方法 不能是
private
、protected
或final
。 -
接口变量是常量(
public static final
),值不可修改。 -
接口可以是空接口(标记接口),如:
public interface Serializable {}
实现该接口的类具有某种“标识性”功能(例如可被序列化)。
接口的作用
1️⃣ 提供功能约定
实现接口的类具有特定功能,例如:
Cloneable
→ 支持克隆Serializable
→ 支持序列化Comparable
/Comparator
→ 支持比较
示例:Cloneable 接口
public class CloneableTest implements Cloneable {@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
2️⃣ 支持多重继承
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)
通过接口引用不同实现类,实现同一方法不同表现:
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)
- 定义一组算法(策略)并封装,调用方通过接口使用不同实现。
// 策略模式示例:多种支付方式// 1. 定义支付策略接口
interface PaymentStrategy {void pay(double amount);
}// 2. 定义不同的支付策略实现
class AlipayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("使用支付宝支付 " + amount + " 元");}
}class WeChatPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("使用微信支付 " + amount + " 元");}
}class CreditCardStrategy implements PaymentStrategy {@Overridepublic 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)
- 用抽象类实现接口并置空方法,新类继承该抽象类,只需覆盖必要方法。
// 定义播放器接口
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 {@Overridepublic void playMp3(String fileName) {System.out.println(" 正在播放 MP3 文件:" + fileName);}
}class Mp4Player extends MediaAdapter {@Overridepublic void playMp4(String fileName) {System.out.println(" 正在播放 MP4 文件:" + fileName);}
}class FlacPlayer extends MediaAdapter {@Overridepublic 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)
- 定义接口规范产品与工厂,通过不同工厂创建不同对象。
// 图形接口
interface Shape {void draw();
}// 具体图形类:圆形、矩形、三角形
class Circle implements Shape {@Overridepublic void draw() {System.out.println("正在画一个圆形 🟢");}
}class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("正在画一个矩形 🟥");}
}class Triangle implements Shape {@Overridepublic void draw() {System.out.println("正在画一个三角形 🔺");}
}// 抽象工厂接口
interface ShapeFactory {Shape createShape();
}// 具体工厂类
class CircleFactory implements ShapeFactory {@Overridepublic Shape createShape() {return new Circle();}
}class RectangleFactory implements ShapeFactory {@Overridepublic Shape createShape() {return new Rectangle();}
}class TriangleFactory implements ShapeFactory {@Overridepublic 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
修饰。
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 内部类()
。
局部内部类
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️⃣ 匿名内部类
没有名字的内部类,常用于简化
一次性类的使用(尤其是回调和线程)。
public class ThreadDemo {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}});t.start();}
}
特征与要点:
- 无类名、无构造方法。
- 直接通过
new 接口/父类()
创建对象并重写方法。 - 常用于:事件监听、线程、回调等。
- 本质上是一个语法糖,用于快速定义临时子类或实现类。
4️⃣ 静态内部类
public class Icheng {static int age = 21;double money = 99.9;static class Friend {public Friend() {// 21System.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 模式 |
核心思想
内部类 = 一种在类中再定义类的机制,强化封装、灵活扩展。
- 成员内部类 → 外部类的“成员”
- 局部内部类 → 方法的“局部变量”
- 匿名内部类 → 一次性“快捷实现类”
- 静态内部类 → 独立于外部类存在的“嵌套类”