并发编程(二十四):单例模式(三):构造方法私有:单例模式的 “第一道防线”

一、构造方法私有 = 杜绝外部 new 对象

Java 中创建对象的核心方式:new XXX() → 本质是调用 XXX 的构造方法

如果把构造方法设为 private:外部类(比如 Test 类)无法访问这个构造方法,自然就写不了 new ServletSingleton()

从根源上杜绝了 "通过 new 随便创建多个对象" 的可能,这是单例模式的基础前提。

公有构造 vs 私有构造

构造方法修饰符 外部能否 new 对象 能否保证单例
public(公有) 可以(new ServletSingleton()) 不能(想创建多少就创建多少)
private(私有) 不可以(编译报错) 能(外部无法通过 new 创建)

反例(公有构造,无法单例):

java 复制代码
public class BadSingleton {
    // 公有构造:外部可以随便 new
    public BadSingleton() {}

    public static void main(String[] args) {
        BadSingleton s1 = new BadSingleton();
        BadSingleton s2 = new BadSingleton();
        System.out.println(s1 == s2); // false,两个不同对象
    }
}

正例(私有构造,杜绝外部 new):

java 复制代码
public class GoodSingleton {
    // 私有构造:外部无法访问
    private GoodSingleton() {}

    public static void main(String[] args) {
        // 编译报错:GoodSingleton() has private access in GoodSingleton
        GoodSingleton s1 = new GoodSingleton(); 
    }
}

二、"为什么单例模式要把构造方法私有?"

单例模式的目标是「一个类只能创建一个对象」,而 Java 中创建对象的核心方式是调用构造方法

如果构造方法是公有的(public),外部代码可以通过 new 类名() 无限创建对象,完全违背单例的核心要求

将构造方法设为私有(private),可以禁止外部类访问构造方法,从根源上杜绝 "通过 new 创建多个对象" 的可能,这是实现单例的基础前提

三、私有构造也有 "漏洞"

虽然私有构造杜绝了外部 new,但通过反射仍能调用私有构造创建对象,所以生产级单例需要加防护:

java 复制代码
private GoodSingleton() {
    // 反射防护:如果已有实例,直接抛异常
    if (INSTANCE != null) {
        throw new RuntimeException("禁止通过反射创建多个单例实例!");
    }
}

构造方法私有是单例模式的基础,可以杜绝外部通过 new 创建多个对象


四、"构造方法私有后,为什么必须写静态方法?"

构造方法私有 → 外部无法 new 对象 → 无法通过对象调用非静态方法 → 必须提供静态方法作为"入口" → 静态方法内部创建/返回单例对象 → 间接调用非静态方法逐段拆解

1. 构造私有 → 外部无法 new 对象

私有构造只对类内部开放,外部代码写 new 类名() 会编译报错

没有对象,就无法调用非静态方法(非静态方法必须依托对象:对象.方法())

2. 为什么必须写 public static 方法?

方法类型 是否依托对象 外部能否调用 (构造私有) 作用
非静态方法 必须依托对象 不能(外部拿不到对象) 单例的核心业务方法(如 doGet/doPost)
静态方法 不依托对象(类级) 能(类名.方法()) 作为外部唯一入口,创建 / 返回单例对象

静态方法是外部访问单例类的唯一通道------ 它不依托对象,能直接通过类名调用

且在类内部,静态方法可以访问私有构造,创建唯一的单例对象

3. 静态方法的 "特殊权限":类内可调用私有构造

java 复制代码
public class ServletSingleton {
    private volatile static ServletSingleton INSTANCE;

    // 1. 私有构造:外部不能new,类内部可以!
    private ServletSingleton() {}

    // 2. 静态方法:外部唯一入口(类名.getInstance())
    public static ServletSingleton getInstance() {
        if (INSTANCE == null) {
            synchronized (ServletSingleton.class) {
                if (INSTANCE == null) {
                    // 静态方法内部:可以调用私有构造(类内权限)
                    INSTANCE = new ServletSingleton(); 
                }
            }
        }
        return INSTANCE; // 返回单例对象
    }

    // 3. 非静态业务方法(外部只能通过单例对象调用)
    public void doGet() {
        System.out.println("处理GET请求");
    }
}

// 外部调用示例(唯一正确方式)
public class Test {
    public static void main(String[] args) {
        // 第一步:通过静态方法拿到单例对象(构造私有,只能这么拿)
        ServletSingleton singleton = ServletSingleton.getInstance();
        // 第二步:通过对象调用非静态方法
        singleton.doGet();
    }
}

五、为什么 Servlet/Controller 不建议全设为静态?

场景 单例类的静态方法(getInstance) Servlet/Controller 的静态方法
静态方法数量 只有 1 个(getInstance) 大量(doGet/doPost/ 各种业务方法)
内存占用 仅 1 个静态方法占内存,可忽略 所有静态方法类加载时就占内存,浪费严重
灵活性 仅入口是静态,核心方法是非静态(可重写 / 继承) 全静态方法无法重写,扩展性差
设计目的 只为提供 "拿对象的入口" 提供业务处理能力,需要灵活扩展

单例类的静态方法只有一个(getInstance),占内存极少,是 "必要之恶"

但 Servlet/Controller 有大量业务方法(doGet/doPost/ 查询 / 新增等),如果全设为静态:

  • 类加载时所有静态方法都占内存,浪费严重
  • 静态方法不能重写,后续扩展(比如子类重写 doGet)完全做不了

六、要点总结

构造方法私有 → 外部无法 new 对象 → 无法通过对象调用非静态方法

静态方法不依托对象,可通过 类名.方法() 直接调用,是外部访问单例类的唯一入口

静态方法在类内部可访问私有构造,能创建并返回唯一的单例对象,间接让外部调用到非静态业务方法

而 Servlet/Controller 不建议全设为静态,因为其业务方法多,全静态会导致类加载时大量占用内存,且静态方法无法重写,扩展性差

相关推荐
starzy19909 小时前
Flink基础之Per-Job-Cluster原理详解:被 Application 取代的模式
java·大数据·flink
魏 无羡16 小时前
webclient
java·webclient
神仙别闹16 小时前
基于 C++ 实现两个有序链表序列的交集
java·c++·链表
swordbob17 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言
m0_5873830018 小时前
全民健身解决方案软件开发实战:从架构设计到落地指南
java·spring boot·spring·架构·需求分析
白山编程大哥18 小时前
Java OutputStreamWriter 详解:从字符到字节的桥梁
java·开发语言·python
一技安身19 小时前
【信创】Docker‑Compose V2 两种离线部署(独立模式、插件模式)简易教程
java·docker·eureka
七夜zippoe20 小时前
为什么 2026 年每个 Java 团队都该懂 AI Agent
java·开发语言·人工智能
时凌云.20 小时前
【2026最新】JDK 下载安装与环境配置全教程(Windows/Mac/Linux 三平台,零基础友好)
java·linux·macos
Despacito100620 小时前
Java后端性能探查工具速查表
java·开发语言