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

一、构造方法私有 = 杜绝外部 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 不建议全设为静态,因为其业务方法多,全静态会导致类加载时大量占用内存,且静态方法无法重写,扩展性差

相关推荐
roman_日积跬步-终至千里5 分钟前
【资源控制】自助查询的智能路由
java·大数据·数据库
步行cgn30 分钟前
MyBatis Error evaluating expression ‘ids‘. Return value (3) was not iterable 错误详
java·后端
lhldsg1 小时前
社区健身场地规划实战指南:从器材配置到智能化管理经验分享
java·开发语言·经验分享·小程序
LayZhangStrive1 小时前
后端通识 - 后端工程开发常用的流程图类别
java·流程图·后端开发
WIN赢1 小时前
【抽象思想-从复杂中抽离简单、收敛的口子】
java·前端·javascript
captain3761 小时前
多线程单例模式
java·单例模式·java-ee
xiaotianyuanma2 小时前
【计算机毕业设计】基于Spring Boot的体育馆预约系统的设计与实现
java·spring boot·课程设计
何以解忧,唯有..2 小时前
MySQL 索引分类与底层原理深度剖析
java
水无痕simon2 小时前
6、六大工作模式-2
java·rabbitmq
2601_963870212 小时前
【计算机毕业设计】基于Spring Boot的非遗文创产品交易平台的设计与实现
java·spring boot·后端