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

首次接触设计模式,如何为含主类与3个继承类的代码实现单例模式?

嘿,别担心,第一次接触设计模式有点懵太正常了!我来一步步帮你搞定单例模式在你的geniusATM类和子类中的实现~

先搞懂单例的核心逻辑

单例模式的本质就是让一个类只能创建出唯一的对象,而且整个程序里都能通过统一入口访问这个对象。对于新手来说,最容易上手且安全的是「饿汉式」实现,我们先从你的主类开始改造。

第一步:给geniusATM主类实现单例

先把你没写完的主类补全并改成单例(顺便调整了下Java规范的命名):

class GeniusATM {
    // 1. 私有化构造方法!禁止外部直接new对象,这是单例的核心前提
    private GeniusATM() {
        // 这里可以初始化默认值,比如余额设为0
        this.checkingBalance = 0.0;
        this.savingBalance = 0.0;
        this.mortgageBalance = 0.0;
    }

    // 2. 创建类级别的唯一静态实例(饿汉式:类加载时就初始化,简单且线程安全)
    private static final GeniusATM INSTANCE = new GeniusATM();

    // 3. 提供全局唯一的访问入口,让外部能拿到这个实例
    public static GeniusATM getInstance() {
        return INSTANCE;
    }

    // 你的成员变量,建议按需求设置访问权限(比如private,再提供get/set方法)
    private String name, address;
    private int pin, ficoScore;
    double checkingBalance, savingBalance, mortgageBalance;

    // 示例业务方法:查询支票账户余额
    public void checkCheckingBalance() {
        System.out.println("当前支票账户余额:" + checkingBalance);
    }

    // 示例业务方法:存款到支票账户
    public void depositToChecking(double amount) {
        if (amount > 0) {
            checkingBalance += amount;
            System.out.println("存款成功!当前余额:" + checkingBalance);
        }
    }

    // 你可以继续添加其他ATM功能方法,比如取款、修改PIN等
}

关键细节说明

  • private GeniusATM():外部代码再也不能用new GeniusATM()创建对象,保证了只有类内部能生成实例
  • private static final GeniusATM INSTANCE:静态变量属于类本身,只会在类加载时初始化一次,彻底确保实例唯一
  • public static getInstance():给外部提供获取实例的唯一通道,所有地方用这个方法拿到的都是同一个对象

第二步:处理3个继承类的单例问题

这里要注意:父类是单例的话,子类默认没法直接继承后做单例——因为父类的构造方法是private,子类无法访问。给你两种实用方案:

方案1:子类各自独立实现单例(适合子类有专属独立功能的场景)

比如你有一个PremiumATM子类,需要单独的单例实例:

class PremiumATM extends GeniusATM {
    // 先调整父类的构造方法权限为protected,这样子类能调用父类构造
    // (把GeniusATM的构造方法从private改成protected即可)
    private PremiumATM() {
        super(); // 调用父类构造
    }

    // 子类自己的单例实例和访问入口
    private static final PremiumATM INSTANCE = new PremiumATM();
    public static PremiumATM getInstance() {
        return INSTANCE;
    }

    // 子类专属功能:比如高级抵押贷款申请
    public void applyPremiumMortgage() {
        System.out.println("正在为您申请高级抵押贷款...");
    }
}

这种方式下,父类和子类各有自己的唯一实例,既能复用父类的功能,又能实现子类的专属逻辑。

方案2:父类单例整合子类功能(适合子类只是父类的功能扩展,不需要独立实例)

如果你的子类只是给主ATM加功能,不需要单独的实例,那用「组合」代替「继承」会更灵活:

// 父类还是保持单例
class GeniusATM {
    // 内部持有子类的服务实例
    private PremiumService premiumService = new PremiumService();

    private GeniusATM() {}
    private static final GeniusATM INSTANCE = new GeniusATM();
    public static GeniusATM getInstance() { return INSTANCE; }

    // 父类原有方法...

    // 把子类的功能通过父类暴露出去
    public void applyPremiumMortgage() {
        premiumService.applyPremiumMortgage();
    }
}

// 子类变成普通服务类,不需要单例
class PremiumService {
    public void applyPremiumMortgage() {
        System.out.println("正在为您申请高级抵押贷款...");
    }
}

这种方式避免了继承单例带来的构造权限问题,代码结构也更清晰。

第三步:怎么调用单例对象的方法?

不管是父类还是子类的单例,调用方式都是通过getInstance()获取实例,然后调用方法:

public class Main {
    public static void main(String[] args) {
        // 获取主ATM的唯一实例
        GeniusATM mainAtm = GeniusATM.getInstance();
        mainAtm.depositToChecking(1500);
        mainAtm.checkCheckingBalance();

        // 如果用子类单例方案
        PremiumATM premiumAtm = PremiumATM.getInstance();
        premiumAtm.applyPremiumMortgage();
        // 也能调用父类的方法
        premiumAtm.depositToChecking(800);
    }
}

新手避坑提醒

  • 绝对不能忘记私有化构造方法!如果构造方法是public,外部还是能随便new对象,单例就白做了
  • 新手优先用饿汉式,别一开始就碰懒汉式(延迟加载),容易踩线程安全的坑
  • 继承单例类时,一定要调整父类构造方法的权限为protected,不然子类会编译报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:14:26