为什么在单例类中有私有构造函数是必需的

8

这是我用来获取数据库连接的单例类。

我有一个问题:为什么在单例类中必须有一个私有构造函数(因为在我的整个应用程序中,我只调用这个类一次),由于可以使用静态方法实现类的一个实例?

这个私有构造函数能否被避免或者说是必须的?

 public class ConnPoolFactory {
        private static DataSource dataSource;
        private static Connection connection;

        private ConnPoolFactory() {
            System.out.println(" ConnPoolFactory cons is called ");
        }

        public static synchronized Connection getConnection() throws SQLException {

            try {

                if (connection == null) {
                    Context initContext = new InitialContext();
                    Context envContext = (Context) initContext
                            .lookup("java:/comp/env");
                    dataSource = (DataSource) envContext.lookup("jdbc/Naresh");
                    connection = dataSource.getConnection();
                } else {
                    return connection;
                }

            } catch (NamingException e) {
                e.printStackTrace();
            }

            return connection;

        }
    }

谢谢你的回答,但是为什么别人会创建我的Singleton类的实例,因为这是我的应用程序,我有完全的控制权? - user1254422
4
只要你不声明数据为私有,其他人就无法访问你的数据,和其他类似:如果你是唯一在项目上工作并完全掌控它的开发人员,那就没问题。但是当其他开发人员开始加入你的项目时,他们可能不知道不能创建更多的类实例。而且顺便说一下,你自己也可能会意外忘记 :) - Vlad
顺便提一下:这不是一个单例,因为你必须在某个地方创建一个实例。你有一个必须也要有一个私有构造函数的实用类。 - Peter Lawrey
10个回答

20
否则每个人都可以创建该类的实例,因此它就不再是单例。根据定义,单例只能存在一个实例。

为什么别人会创建我的单例类的实例,因为这是我的应用程序,我有完全的控制权? - user1254422
7
有一天,你永远不知道你的应用程序会发生什么。项目可能会扩大,并吸引更多的开发人员加入其中。因此,只要你的项目非常小,而且你对其拥有完全控制权,你可以按照自己喜欢的方式进行操作,但是一旦项目变得更加复杂,你需要执行“单例模式唯一”的规则。 - Vlad
2
@user1254422 - 大多数人都是在团队中工作,所以才会说“其他人”。但是,即使只有你一个人,如果不小心的话也可能会创建多个实例。私有构造函数是一种安全机制,可以防止多个实例的出现,因为创建实例的唯一方式是通过静态获取方法。 - Erik Funkenbusch
@user1254422:同样地,如果您对项目拥有完全的控制权,您不需要声明数据为私有的,对吗?但是一旦项目变得更加庞大,您就必须开始强制执行规则。 - Vlad

8
如果没有私有构造函数,Java会为您提供一个默认的公共构造函数。然后您可以多次调用此构造函数来创建多个实例。那么它就不再是单例类了。

8
如果您不需要懒加载:
public class Singleton {
    private static final Singleton instance = new Singleton();

    // Private constructor prevents instantiation from other classes
    private Singleton() { }

    public static Singleton getInstance() {
            return instance;
    }
}

使用synchronized是最好的方式,因为它是线程安全的。


6
单例模式通常包括一个非公共构造函数,有两个原因。必须存在构造函数,因为如果根本没有构造函数,则会包含一个公共默认构造函数。然而,如果您有一个公共构造函数,人们可以自己随意创建单例(某个人不可避免地会这样做,这意味着可能会有多个单例)。
构造函数并不一定要是私有的。实际上,据我所知,GoF 指定的 Singleton 模式提到了一个受保护的构造函数,出于某种奇怪的原因。听说是关于继承的问题,但单例和继承根本不搭配。
甚至可以有一个公共构造函数,只要它能检测是否已经存在一个实例,如果存在则抛出异常或其他错误。那就足以保证单例。但这样做比较罕见,因为通过提供两种明显的获取实例的方式来复杂化事情,其中只有一种实际起作用。如果您不希望外部代码能够创建第二个实例,那么为什么构造函数应该成为公共接口的一部分呢?

4

在单例模式中,使用私有构造函数以确保不会创建其他实例,否则它将不是单例。

要了解更多关于单例模式的信息,请查看相关链接。


3
一个静态类和单例类不同在于单例类强制限制最多只有一个实例。静态类没有实例,仅是一组静态函数和静态数据。
因此,对于单例类,即至多只有一个实例的类,需要一个私有构造函数。
在您的示例中,单例类似乎比静态类更合适,因为它具有连接和dataSource成员。将这些成员设为私有,将构造函数设为私有,并提供引用静态ConnPoolFactory实例的静态方法。如果instance为null,则创建一个新实例,否则只需使用它。

3

对于单例和工具类,您可以使用enum,它是一个final类,并隐式定义了一个私有构造函数。

enum Singleton {
     INSTANCE
}

或者

enum Utility {;

}

在你上面的例子中,你有一个实用类,因为你没有实例字段、方法并且不创建一个实例。

2

你说的评论是,如果我完全拥有我的应用程序,并且永远不会犯直接使用公共构造函数创建单例类实例的错误,而是使用静态方法获取它。但在现实世界中,应用程序经常在多个开发人员之间切换。如果新的开发人员不知道你想要在应用程序中仅有一个类的实例,他/她可能会通过使用公共构造函数意外地创建另一个实例。


1

这是强制性的。你的代码可能没有问题,但其他人可以使用这段代码并实例化另一个对象,或者你可能会不小心这样做。这种方式更加安全。


1

单例模式定义:

确保在整个主堆栈中(每个主类)只需要创建一个对象。

如果要满足上述语句,我们应该将构造函数设置为私有。实际上,通过单例模式,我们获取的是引用而不是对象,因此不必要求在其他类中创建对象,但是我们无法访问引用(构造函数为公共)。

例如:

public class SingleTon {

    private static SingleTon s;
    public SingleTon() {}

    public static SingleTon getInstance() {
        if (s == null) {
            s = new SingleTon();
            System.out.println("ho ho");
        } else{
            return s;
        }
        return s;
    }
}

其他类:

public class Demo {

    public static void main(String[] args) {
        //SingleTon s=SingleTon.getInstance();

        SingleTon s11=new SingleTon();
        SingleTon s12=new SingleTon();
        s11.getInstance();
        s12.getInstance();
    }
}

输出:

ho ho

不可能使用公共构造函数创建单例,因为这将创建新实例。这违背了单例的目的。 - singapore saravanan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接