首次接触设计模式,如何为含主类与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
相关产品推荐
相关产品推荐

