0

0

如何在抽象基类的 main 方法中动态实例化并调用具体子类

聖光之護

聖光之護

发布时间:2026-02-05 14:20:01

|

907人浏览过

|

来源于php中文网

原创

如何在抽象基类的 main 方法中动态实例化并调用具体子类

本文介绍一种专业、可行的方式,让抽象基类 `calculation` 的 `main` 方法能自动发现并实例化所有继承它的具体子类(如 `mone`、`mtwo`),从而实现 `java mone 5` 等命令直接输出对应计算结果。核心在于运行时类路径扫描与反射实例化。

要实现在抽象基类 Calculation 的 main 方法中调用不同子类的 multiply() 方法,关键在于脱离“硬编码 new”,转而采用运行时动态发现 + 反射创建实例的策略。因为 main 方法位于抽象类中,它无法直接 new Calculation(),但可通过类路径扫描识别所有非抽象子类,并逐一实例化执行。

✅ 推荐方案:使用 ClassGraph 实现子类自动发现

ClassGraph 是一个高性能、轻量级的类路径扫描库,可安全、高效地枚举继承自指定基类的所有具体类(即非抽象、非接口的子类)。以下是完整可运行的改进版 Calculation 类:

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;

import java.util.List;

public abstract class Calculation {
    public abstract int multiply(int x);

    public static void main(String[] args) {
        if (args.length == 0) {
            System.err.println("Usage: java  ");
            return;
        }

        try {
            // 解析输入参数(取第一个作为待计算数值)
            int input = Integer.parseInt(args[0]);

            // 扫描所有 Calculation 的具体子类
            List> subclasses;
            try (ScanResult scanResult = new ClassGraph()
                    .enableClassInfo()
                    .acceptPackages("your.package.name") // ? 替换为你的实际包名,提升性能与安全性
                    .scan()) {

                subclasses = scanResult
                        .getSubclasses(Calculation.class.getName())
                        .filter(classInfo -> !classInfo.isAbstract()) // 排除抽象子类(如有)
                        .loadClasses();
            }

            // 对每个具体子类实例化并调用 multiply
            for (Class clazz : subclasses) {
                try {
                    Calculation instance = clazz.getDeclaredConstructor().newInstance();
                    int result = instance.multiply(input);
                    System.out.println(clazz.getSimpleName() + ": " + result);
                } catch (Exception e) {
                    System.err.println("Failed to instantiate or invoke " + clazz.getName() + ": " + e.getMessage());
                }
            }

        } catch (NumberFormatException e) {
            System.err.println("Invalid number argument: " + args[0]);
        }
    }
}
? 重要注意事项:✅ 子类(如 MOne)必须提供无参构造函数(默认即满足),否则 newInstance() 或 getDeclaredConstructor().newInstance() 会抛出异常;✅ 建议通过 .acceptPackages(...) 显式限定扫描范围,避免全类路径扫描带来的性能开销和潜在安全风险;✅ 若子类位于模块化环境(Java 9+),需确保 module-info.java 中 opens 相应包以支持反射访问;❌ 不推荐使用已弃用的 Class.newInstance()(Java 9+ 已标记为 deprecated),应统一使用 getDeclaredConstructor().newInstance()。

? 为什么不建议“在 main 中直接 new 子类”?

你可能会想到在 Calculation.main() 中写类似 new MOne().multiply(x) 的代码——但这违背了面向对象的开放封闭原则(OCP):每新增一个子类(如 MFour),就必须修改基类源码,丧失可扩展性。而上述 ClassGraph 方案实现了零侵入式扩展:只要新子类编译后在类路径中,且继承 Calculation,即可被自动识别并执行。

✅ 验证方式(终端命令)

确保所有类(Calculation, MOne, MTwo, MThree)已编译,并在类路径中:

拍我AI
拍我AI

AI视频生成平台PixVerse的国内版本

下载
# 运行基类 main —— 它会自动发现并调用所有子类
java -cp ".:classgraph-4.8.167.jar" Calculation 5
# 输出示例:
# MOne: 5
# MTwo: 10
# MThree: 15

? 提示:若你坚持按 java MOne 5 方式启动(即子类各自拥有 main),则无需改动 Calculation;只需在每个子类中复用逻辑即可。但本文聚焦于单入口、多策略、自动发现这一更工程化的架构需求。

综上,通过 ClassGraph + 反射 + 显式包过滤,我们既保持了抽象基类的通用性,又赋予其智能化调度子类的能力——这是构建可插拔计算引擎、策略工厂或命令行工具链的坚实基础。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

55

2025.11.27

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

55

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1258

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

275

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2195

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

33

2026.01.19

java中fail含义
java中fail含义

本专题整合了java中fail的含义、作用相关内容,阅读专题下面的文章了解更多详细内容。

0

2026.02.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3.2万人学习

C# 教程
C# 教程

共94课时 | 8.6万人学习

Java 教程
Java 教程

共578课时 | 57.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号