Java中的构造函数可以是私有的吗?

250

构造函数可以是私有的吗?私有构造函数有什么用处?


10
需要注意的是,迄今为止给出的大多数答案只考虑了仅具有私有构造函数的情况。您可以在同一类中拥有公共和私有构造函数,以达到 Michael Aaron Safyan 的答案所解释的目的。 - Christian Semrau
@ChristianSemrau 我正在通过Eclipse查看 lwjgl3 (beta)的jar文件,我在 org.lwjgl.Version 中发现了一个私有构造函数。这就是我来到这里的原因。有趣的是,这个类还有它自己的 main 。Java很奇怪。 - Braden Best
当你尝试过它或查找过相关信息时,发生了什么?是否有理由不信任编译器或 JLS 规范? - user207421
16个回答

169
是的,构造函数可以是私有的。这有不同的用途。其中之一是用于单例设计反模式,我建议你不要使用它。另一个更合法的用途是在委托构造函数中;你可以有一个构造函数,它接受许多不同的选项,但实际上是一个实现细节,所以你将其设为私有,然后你的其余构造函数委托给它。
作为委托构造函数的示例,以下类允许您保存值和类型,但它只允许您对一部分类型进行操作,因此使通用构造函数变为私有是必要的,以确保仅使用允许的类型。常见的私有构造函数有助于代码重用。
public class MyClass {
     private final String value;
     private final String type;

     public MyClass(int x){
         this(Integer.toString(x), "int");
     }

     public MyClass(boolean x){
         this(Boolean.toString(x), "boolean");
     }

     public String toString(){
         return value;
     }

     public String getType(){
         return type;
     }

     private MyClass(String value, String type){
         this.value = value;
         this.type = type;
     }
}

编辑
几年后看这个答案,我想指出这个答案既不完整又有点过激。单例确实是一种反模式,在可能的情况下通常应该避免使用;然而,除了单例之外,还有许多私有构造函数的用法,我的回答只列举了其中一个。

再举几个使用私有构造函数的案例:

  1. To create an uninstantiable class that is just a collection of related static functions (this is basically a singleton, but if it is stateless and the static functions operate strictly on the parameters rather than on class state, this is not as unreasonable an approach as my earlier self would seem to suggest, though using an interface that is dependency injected often makes it easier to maintain the API when the implementation requires larger numbers of dependencies or other forms of context).

  2. When there are multiple different ways to create the object, a private constructor may make it easier to understand the different ways of constructing it (e.g., which is more readable to you new ArrayList(5) or ArrayList.createWithCapacity(5), ArrayList.createWithContents(5), ArrayList.createWithInitialSize(5)). In other words, a private constructor allows you to provide factory function's whose names are more understandable, and then making the constructor private ensures that people use only the more self-evident names. This is also commonly used with the builder pattern. For example:

    MyClass myVar = MyClass
        .newBuilder()
        .setOption1(option1)
        .setOption2(option2)
        .build();
    

7
从可测试性的角度看,它们代表了难以预测(和测试)的全局状态。 - Bozho
13
@Vuntic,简短的回答是单例会导致共享可变状态,更重要的是将单例属性嵌入到API中会使其不灵活,难以测试,并且当单例假设不正确时会造成很多麻烦。虽然只实例化对象一次是可以接受的,但是通过私有构造函数和静态实例化函数来强制实现单例会导致非常混乱和脆弱的设计。更好的方法是传递单例符合的接口。 - Michael Aaron Safyan
7
这种方法被称为依赖注入,其思想是只构造一个实例然后在程序中传递。这样可以使设计更加灵活、简洁。 - Michael Aaron Safyan
24
在我看来,真正的反模式是过度使用。这适用于任何形式的模式,如果没有考虑到给定情况下的最佳选择就习惯性地使用它们。 - rsp
5
谷歌前首席Java架构师乔舒亚·布洛赫(Joshua Bloch)在《Effective Java》第2版中提倡使用私有构造函数的静态工厂方法...考虑到他对该语言的专业知识以及他创建了Java集合框架,我认为私有构造函数与静态工厂方法结合是私有构造函数非常可行且值得推荐的用法。 - Zack Macomber
显示剩余12条评论

111

我本以为有人会提到这一点(第二点),但是...私有构造函数有三个用途:

  • 防止在以下情况下在对象外部实例化:

    • 单例
    • 工厂方法
    • 仅包含静态方法的(实用)类
    • 仅包含常量的类
      .
  • 防止子类化(扩展)。如果只有一个私有构造函数,那么没有任何类可以扩展你的类,因为它无法调用super()构造函数。这在某种程度上相当于final

  • 重载构造函数 - 由于重载方法和构造函数,一些可能是私有的,一些可能是公共的。特别是在存在非公共类的情况下,您可能会创建一个公共构造函数来创建该类的实例,然后将其传递给私有构造函数。


10
在Java中,“final”关键字用于防止子类化;为此,将构造函数设为私有并不是必需的。 - Michael Aaron Safyan
13
不必要,但你可以这样做。我提到了 final 关键字作为实现这一点的一种方式。 - Bozho
1
“以下情况”必然是不完整的,但在任何情况下都是无关紧要的。它防止在所有情况下从外部实例化,而不仅仅是在此处列举的情况。 - user207421
@MichaelAaronSafyan,这并不完全正确。抽象类可能不是final(Java 8)。如果一个类通过静态方法提供了所有的实现(子类),而我希望它在这个意义上是final的,那么为什么我还要将其定义为抽象类呢? - Werner Erasmus

27

可以实现。私有构造函数存在的目的是防止类被实例化,或者因为构造仅在内部发生,例如工厂模式。有关更多信息,请参见此处


18

是的。

这样做可以控制类的实例化方式。如果将构造函数设为私有,然后创建可见的构造方法用于返回类的实例,你就可以限制实例的数量(通常保证只有一个实例存在),或者回收实例或其他与构造相关的任务。

使用 new x() 永远不会返回 null,但是使用工厂模式,可以返回 null,甚至返回不同的子类型。

你也可以用于一个没有实例成员或属性,只有静态成员的类——比如一个实用函数类。


但是你可以通过使用静态变量来跟踪实例的数量,而不必将构造函数设为私有。 - Michael Aaron Safyan
1
@michael 确实可以这样做,但不够优雅,而且对于类的用户来说限制不够明显。 - Jon
1
@Jon,抱歉,我误解了...我以为你只是在计算实例的数量,而不是限制实例的数量。 - Michael Aaron Safyan

10

如果一个类中的所有方法都是静态的,那么使用私有构造函数是一个很好的选择。


8

7

在Java中定义私有构造函数的原因如下:

  1. 控制Java对象的实例化,防止创建对象的实例。

  2. 防止类被子类化。

  3. 在实现单例模式时具有特殊优势,使用私有构造函数可以控制整个应用程序的实例创建。

  4. 当您想要定义所有常量并且不再需要其实例的类时,我们将该类声明为私有构造函数。


它们可以因任何原因声明,或者根本没有理由,只是实现者的心血来潮。 - user207421
私有构造函数不能防止子类化 (https://self-learning-java-tutorial.blogspot.com/2018/05/can-private-constructor-prevent-sub.html) - Hari Krishna

5

是的。

私有构造函数用于防止实例初始化,例如您在Java中使用的Math final类。单例模式也使用私有构造函数。


3
一个构造函数可以是私有的吗?私有构造函数有什么用处?
可以。我认为这是另一个有用的例子:
//... ErrorType.java
public enum ErrorType {
    X,
    Y,
    Z
}

//... ErrorTypeException.java
import java.util.*;
import java.lang.*;
import java.io.*;

//Translates ErrorTypes only
abstract public class ErrorTypeException extends Exception {
    private ErrorTypeException(){}

    //I don't want to expose thse
    static private class Xx extends ErrorTypeException {}
    static private class Yx extends ErrorTypeException {}
    static private class Zx extends ErrorTypeException {}

    // Want translation without exposing underlying type
    public static Exception from(ErrorType errorType) {
        switch (errorType) {
            case X:
                return new Xx();    
            case Y:
                return new Yx();
            default:
                return new Zx();
        }
    }

    // Want to get hold of class without exposing underlying type
    public static Class<? extends ErrorTypeException> toExceptionClass(ErrorType errorType) {
        switch (errorType) {
            case X:
                return Xx.class;    
            case Y:
                return Yx.class;
            default:
                return Zx.class;
        }
    }
}

在这种情况下,它防止抽象类被除了其静态内部类之外的任何派生类实例化。抽象类不能是final的,但在这种情况下,私有构造函数使得它对于所有不是内部类的类来说都是有效的final。

3

是的,构造函数可以是私有的。私有构造函数防止任何其他类实例化私有构造函数的示例。

public class CustomHttpClient {
private static HttpClient customHttpClient;

/** A private Constructor prevents any other class from instantiating. */
private CustomHttpClient() {
}}

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