JavaSE面向对象(下)

抽象类的定义

  • 使用 abstract 关键字修饰类:

    java 复制代码
    abstract class AbstractPlayer {}
  • 按规范,抽象类命名应以 AbstractBase 开头(如:AbstractPlayerBaseService)。

抽象类的特征

  1. 不能被实例化

    java 复制代码
    AbstractPlayer player = new AbstractPlayer(); // ❌ 编译错误
  2. 可以被继承 :子类使用 extends 继承抽象类

    java 复制代码
    public class BasketballPlayer extends AbstractPlayer {}
  3. 包含抽象方法

    抽象方法必须在抽象类中定义

    抽象方法没有方法体,只定义方法签名

    子类必须实现父类中的所有抽象方法,否则子类也必须是抽象类

    java 复制代码
    abstract void play();
  4. 可以包含普通方法:抽象类可以混合抽象与非抽象方法

    java 复制代码
    public abstract class AbstractPlayer {
        abstract void play();
        public void sleep() {
            System.out.println("运动员也要休息");
        }
    }

抽象类的应用场景

1️⃣ 代码复用场景

  • 抽象类可以定义多个子类共有的功能 (如 sleep() 方法),避免重复代码。
  • 子类继承后可以直接使用这些公共方法。

2️⃣ 统一接口、延迟实现场景

  • 抽象类可作为一种模板或框架,规定子类必须实现的功能。

  • 例如:play() 方法定义了所有运动员都要"从事某项运动"的行为,但不同子类各自实现不同内容。

    java 复制代码
    abstract 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 关键字定义接口:

    java 复制代码
    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 可有方法体,供实现类直接复用

接口可包含静态方法与默认方法,用以增强接口的扩展性和兼容性。

接口的限制与特性

  1. 接口 不能被实例化 ,必须由类通过 implements 实现:

    java 复制代码
    public class Computer implements Electronic { ... }
  2. 接口 不能使用 final 修饰(否则无法被实现)。

  3. 抽象方法 不能是 privateprotectedfinal

  4. 接口变量是常量(public static final),值不可修改

  5. 接口可以是空接口(标记接口),如:

    java 复制代码
    public 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());
}

多态的三个前提

  1. 有继承或实现关系;
  2. 子类重写父类或接口方法;
  3. 父类引用指向子类对象。

接口的三种典型设计模式

策略模式(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() 方法一致,方法执行完就销毁
  • 不能使用 publicprivateprotectedstatic 修饰。
  • 常用于在方法中临时封装逻辑或实现接口。

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》:

"使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现。"

内部类的优势

  1. 实现多重继承效果
    内部类可以在外部类继承某个类的同时,实现另一个接口或类的功能。
  2. 实例独立性
    每个内部类对象都可拥有自己的状态,与外部类实例独立。
  3. 多样实现同一接口
    在一个外部类中,可以有多个内部类分别以不同方式实现同一个接口。
  4. 延迟创建
    内部类对象可在任意时刻创建,不依赖外部类的初始化。
  5. 更好的封装性
    内部类可隐藏实现细节,对外部完全不可见。

对比表

类型 定义位置 是否静态 是否依附外部类对象 可访问外部类成员 修饰符限制 使用场景
成员内部类 外部类成员处 所有成员 无限制 外部类的辅助逻辑
局部内部类 方法/作用域内 可访问局部变量(final 或 effectively final) 不能用 public/private/protected/static 方法内临时使用
匿名内部类 表达式内 可访问外部成员 无修饰符 简化接口或抽象类实现
静态内部类 外部类成员处 仅可访问静态成员 可用 public/private/protected 辅助类、工具类、Builder 模式

核心思想

内部类 = 一种在类中再定义类的机制,强化封装、灵活扩展。

  • 成员内部类 → 外部类的"成员"
  • 局部内部类 → 方法的"局部变量"
  • 匿名内部类 → 一次性"快捷实现类"
  • 静态内部类 → 独立于外部类存在的"嵌套类"
相关推荐
Madison-No73 小时前
【C++】探秘vector的底层实现
java·c++·算法
偶尔的鼠标人3 小时前
Avalonia DataGrid 控件的LostFocus事件会多次触发
开发语言·c#
晚风残3 小时前
【C++ Primer】第十二章:动态内存管理
开发语言·c++·c++ primer
我登哥MVP3 小时前
Ajax 详解
java·前端·ajax·javaweb
vue学习3 小时前
docker 学习dockerfile 构建 Nginx 镜像-部署 nginx 静态网
java·学习·docker
_extraordinary_3 小时前
Java Spring日志
java·开发语言·spring
PHP源码3 小时前
SpringBoot校园二手商城系统
java·spring boot·springboot二手商城·java校园二手商城系统
我命由我123453 小时前
Android 开发问题:getLeft、getRight、getTop、getBottom 方法返回的值都为 0
android·java·java-ee·android studio·android jetpack·android-studio·android runtime