You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何跨类定义并调用列表供BlockBreakEvent监听器使用?

问题优化方案

你当前的写法确实存在性能和可维护性问题:每次触发方块破坏事件都会重新创建ArrayList实例、重复添加所有方块元素,事件高频触发时会产生大量无用临时对象增加GC压力,同时方块列表硬编码在事件方法内,后续修改规则需要翻业务逻辑代码,维护成本很高。
注意:你原代码存在逻辑bug:allowedblocks.contains(block)返回true时代表方块在允许列表内,你当前逻辑反而会取消破坏事件,和注释描述的规则相反,后续代码已修正该问题。

方案1:最简实现(适合小型插件,无额外复杂度)

不需要引入复杂框架,直接创建独立类存储全局规则列表即可,列表在类加载时仅初始化一次,全局唯一可直接调用。
首先创建独立的规则常量类:

// 全局方块规则存储类
public final class BlockRules {
    // 静态不可变列表,类加载时初始化,仅创建一次
    public static final List<Material> ALLOWED_BREAK_BLOCKS = List.of(
            Material.STONE,
            Material.DIRT,
            Material.COBBLESTONE
            // 直接追加需要的方块类型即可,无需重复写add方法
    );

    // 私有构造方法,禁止实例化工具类
    private BlockRules() {}
}

修改监听器代码,移除方法内的列表创建逻辑,直接调用全局列表判断:

public class BlockBreakListener implements Listener {
    @EventHandler
    public void onBreak(BlockBreakEvent e) {
        Player player = e.getPlayer();
        Material brokenBlock = e.getBlock().getType();

        if (BlockRules.ALLOWED_BREAK_BLOCKS.contains(brokenBlock)) {
            player.sendMessage("有效方块,允许破坏");
            return;
        }

        player.sendMessage("无效方块,破坏已取消");
        e.setCancelled(true);
    }
}

该方案优缺点:

  • 优点:代码量最小,无学习成本,完全解决重复创建列表的性能问题,修改规则直接去BlockRules类调整即可
  • 缺点:静态常量耦合度较高,不支持运行时热重载规则,适合规则固定的小型插件

方案2:依赖注入实现(适合中大型插件,支持扩展/热重载)

不需要专门引入第三方DI框架,通过构造方法传参就是最朴素的依赖注入实现,这种方式完全解耦规则存储和事件逻辑,后续支持从配置文件、数据库加载规则,也可以做热重载不用重启服务器。
首先创建独立的规则管理器类,不使用静态变量:

public class BlockRuleManager {
    private Set<Material> allowedBreakBlocks;

    public BlockRuleManager() {
        // 插件启动时初始化一次规则
        loadRules();
    }

    private void loadRules() {
        // 这里既可以硬编码方块,也可以改成从config.yml等配置文件读取
        this.allowedBreakBlocks = new HashSet<>(List.of(
                Material.STONE,
                Material.DIRT,
                Material.COBBLESTONE
        ));
    }

    // 提供重载方法,后续可通过命令触发配置热更
    public void reloadRules() {
        loadRules();
    }

    // 对外只提供判断方法,不直接暴露内部集合,避免外部误修改规则
    public boolean canBreakBlock(Material material) {
        return allowedBreakBlocks.contains(material);
    }
}

在插件主类中初始化全局唯一的规则管理器实例,注册监听器时将实例传入(即完成依赖注入):

public class YourPlugin extends JavaPlugin {
    private BlockRuleManager blockRuleManager;

    @Override
    public void onEnable() {
        // 插件启动时仅初始化一次规则管理器
        blockRuleManager = new BlockRuleManager();
        // 注册监听器时传入已初始化的管理器实例
        getServer().getPluginManager().registerEvents(new BlockBreakListener(blockRuleManager), this);
    }

    // 对外提供管理器实例,供重载命令等其他模块调用
    public BlockRuleManager getBlockRuleManager() {
        return blockRuleManager;
    }
}

修改监听器,通过构造方法接收注入的规则管理器实例:

public class BlockBreakListener implements Listener {
    private final BlockRuleManager blockRuleManager;

    // 构造方法接收注入的依赖
    public BlockBreakListener(BlockRuleManager blockRuleManager) {
        this.blockRuleManager = blockRuleManager;
    }

    @EventHandler
    public void onBreak(BlockBreakEvent e) {
        Player player = e.getPlayer();
        Material brokenBlock = e.getBlock().getType();

        if (blockRuleManager.canBreakBlock(brokenBlock)) {
            player.sendMessage("有效方块,允许破坏");
            return;
        }

        player.sendMessage("无效方块,破坏已取消");
        e.setCancelled(true);
    }
}

该方案优缺点:

  • 优点:规则逻辑和事件逻辑完全解耦,列表仅在插件启动/重载时初始化,无额外性能开销;支持热重载配置,后续修改规则加载逻辑不需要改动事件代码,可测试性更强
  • 缺点:相比静态常量写法代码量稍高

性能优化提示

如果你的规则列表方块数量超过30个,建议用HashSet替代ArrayList存储方块类型:HashSet的contains查询时间复杂度为O(1),远快于ArrayList的O(n)遍历查询,事件高频触发时性能差距会非常明显,两种集合的contains方法调用写法完全一致,不需要修改业务判断逻辑。

内容的提问来源于stack exchange,提问作者user19387122

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:24:22