使用私有构造函数来防止类的实例化?

20

目前我正在考虑给一个只包含一些String常量的类添加私有构造函数。

public class MyStrings {
  // I want to add this:
  private MyString() {}

  public static final String ONE = "something";
  public static final String TWO = "another";
  ...
}

如果我添加一个私有构造函数来防止某人实例化这个类,会有任何性能内存开销吗?

您认为这是否有必要,或者说出于这个目的而使用私有构造函数只是浪费时间和代码?

更新

我将创建一个带有私有构造函数并附带描述性Javadoc文档的final类。我不能使用枚举(虽然我更喜欢使用枚举),因为我现在被困在Java 1.4上。这将是我的修改:

/**
 * Only for static access, do not instantiate this class.
 */
public final class MyStrings {
  private MyString() {}

  public static final String ONE = "something";
  public static final String TWO = "another";
  ...
}

更新块是一个答案。 - 030
9个回答

14

如何使用私有构造函数防止类的实例化?

有几种方法可以防止用户创建常量时进行实例化:

  1. 正如你所提到的,一个带有私有构造函数和所有字符串常量的类是一种方法,即使有一定的开销,但可以忽略不计。
  2. 否则,您可以创建一个带有final修饰符的类,并定义您的字符串常量。
  3. 您可以使用抽象类来定义字符串常量。
  4. 您可以在属性文件中定义字符串常量,并从中访问,这将显著减少内存占用并增加代码的灵活性。

10
对我来说,最好的解释在《Effective Java》一书中:第四条:使用私有构造函数强化非实例化。(更多信息
总之:
  • 私有构造函数是由于实用工具类无需被设计为实例化,因此这是一个设计决策(不会带来性能或内存开销)。
  • 将类设置为抽象类不起作用,因为它可以被子类继承并实例化。
  • 对于抽象类,用户可能认为该类是为了继承而设计的。
  • 唯一确保不实例化的方法是添加一个private constructor,它确保不生成默认构造函数。
  • 私有构造函数防止继承,因为超级构造函数不能被调用(因此不需要声明类为final)。
  • 在私有构造函数中抛出错误可避免在类内调用它。
明显,最好的方式是像下面这样:
public class MyStrings {

     private MyStrings () {
       throw new AssertionError();
     }
      ... 
 }

8

你可以添加一个私有构造函数,但还有两个其他选项。

在同样的情况下,我会使用枚举。如果它对你的实现有意义,你可以使用它替代,如果它是publicprivate取决于你需要在哪里使用它:

public enum MyStrings {

  ONE ("something"),

  TWO ("something else");

  private String value;

  private MyStrings(String str) {
     this.value = str;
  }

}

另一种选择是将其放入一个抽象类中,这些类无法实例化:

public abstract MyStrings {

  public static final String STUFF = "stuff";
  public static final String OTHER = "other stuff";
}

对于枚举器和抽象类,访问方式与您提供的实现方式相同:

MyStrings.STUFF

1
我也认为在这里使用枚举是正确的选择。我还会重写toString方法。 - Eldelshell
啊,我忘了提到这个类是用于Java 1.4的。我希望它能在1.5+中使用,但你知道,你并不总是得到你想要的... ;-)我会选择使用ENUM和一个新版本的final类。 - cringe

5

如果您不希望任何人创建该类的对象,则可以将其声明为抽象类,如下所示

public abstract class MyStrings {
  public static final String ONE = "something";
  public static final String TWO = "another";
}

并且可以像这样访问您的静态变量

String val1 = MyStrings.ONE;
String val2 = MyStrings.TWO;

我认为这将是一个更好的解决方案。

2
但是您仍然可以扩展该类,这在final类中是不允许的。添加私有构造函数也将防止实例化,然后您就完成了。 - b_erb
是的,你说得对,它可以被扩展,你有一点。我没有想到过这个问题,最终类和私有构造函数是一个不错的解决方案。 - jweber
我会选择这种方法。如果有人费心扩展抽象类并实例化它,那么他一定有非常好的理由这样做。 - Rosdi Kasim
1
@Rosdi:你的乐观精神真是让人耳目一新! - Joachim Sauer

2

我更愿意使用枚举来保存这些字符串。这将确保无论在哪里使用这些字符串,你只会传入允许的字符串之一。


2
在这种情况下,如果添加一个私有构造函数,则不会产生性能或内存开销。同样,这也不是必需的,因为您的公共静态变量在对象的所有实例之间共享。

1
如果你的类只包含静态成员,那么就不需要有私有或公共构造函数。所有成员即使没有对象也都是可以访问的。事实上,我发现在这种情况下有一个构造函数会令人感到困惑。

11
如果您没有构造函数,编译器将会提供一个 - 这个构造函数将是公共的,允许人们构造一个实例。私有构造函数的目的是为了防止这种情况发生。 - Jon Skeet
1
@Jon在提供的情况下没有任何意义,因为所有成员都是静态和最终的。对于这个类的实例,我们无法做太多事情,因此防止它被实例化是没有意义的。嗯,它可以防止别人使用扩展类,但是“final”类可以解决这个问题。 - jweyrich
4
防止实例化的目的在于防止用户进行无意义的操作。这就是为什么C#中存在静态类的原因。如果从来没有创建类的实例的必要性,你的代码应该防止它发生。在私有构造函数后加上"// 防止实例化"的注释即可。 - Jon Skeet
1
@jweyrich:假设有人确实实例化了它。他们误用了你的类。你不想打击这种行为吗?@kgiannakakis:这就是为什么我会包含注释的原因 :) (在javadoc中也会有适当的注释。) - Jon Skeet
@Jon 哦,请把这个人远离刀枪!;-) 已经明白了。 - jweyrich
显示剩余4条评论

0

无论如何都会生成一个合成公共构造函数。所以不需要。

在数亿次运行中,几个字节并不会有太大的差别。

我还建议将类声明为final,并且出于完整性考虑,让构造函数抛出异常。

如果你想要源代码简短,可以创建一个没有值的枚举。不过这可能会让初学者程序员感到困惑。


1
只有在没有其他构造函数时,Java才会合成默认的公共构造函数。私有构造函数会阻止默认构造函数的生成。 - Donal Fellows

0

这是存储常量的正确方式,也建议在Effective Java(第2版),第19项中使用。


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