【问题标题】:Preventing call of a private constructor from within the class in Java防止从Java中的类中调用私有构造函数
【发布时间】:2010-05-23 19:04:01
【问题描述】:

我们可以通过将构造函数设为私有来限制类对象的创建。 但是这个构造函数仍然可以从类中调用。 在 Java 中有什么方法可以防止这种情况发生吗?

谢谢。

【问题讨论】:

  • 您可以使用 Aspectj 来确保它。但是,除非您希望在多个地方这样做,否则只建议这样做,因为它会增加巨大的依赖性和复杂性。通常情况下,标准和实践会比工具和控制更好地为您服务。
  • 我们从不打算实例化的私有构造函数中抛出 UnsupportedOperationException。实际上,这个习惯用法对我们来说很常见,以至于我们的 IDE 被配置为输入 pct(四个按键),它会生成一个抛出 UnsupportedOperationException 的私有构造函数。

标签: java constructor singleton private-constructor


【解决方案1】:

不,没有干净的方法可以做到这一点。我真的看不出这样做的理由。因为如果构造函数是私有的,这意味着他只能从这个确切类中的代码中调用(没有子类,或者包中的其他类),所以如果你不希望构造函数被调用,请在其上添加注释是这么说的。

由于每个能够使用构造函数的人都可以删除您为阻止调用构造函数而设置的任何措施,因此不会产生实际效果。

此外,如果您需要单例,那么您可能希望构造函数至少运行一次(除非您认为单例是纯静态类)。

【讨论】:

  • 抛出异常会阻止它,这是一种干净的方式。你甚至可以为它编写测试。任何人都可以从类内或通过反射调用它,并且仍然会抛出异常。所以这样做是有原因的:反思。
【解决方案2】:

如果它是私有的,并且您不希望它在自己的类中被调用,有一种简单的方法可以防止这种情况发生:不要编写多次调用它的代码。您是该课程的作者。你为什么要编写代码来阻止你自己做一些你可以控制的事情?

如果不借助反射,就不能在类外调用它。如果您不想在课堂上多次调用它,那么就不要这样做。为什么要编写代码来阻止您可以选择不执行的操作?

让我们回顾一下:

我们可以限制对象的创建 类的构造函数 私人的。

构造函数是私有的,因此不能在类外调用,除非客户端使用反射 API 破坏访问限制。

但是这个构造函数仍然可以 从类调用。有没有 无论如何要在 Java 中防止这种情况?

OP 要求阻止在类中调用受限构造函数。

如果这两个陈述是正确的,请解释为什么需要有逻辑来阻止对构造函数的调用。

下面是私有构造函数的典型用法:

public class Singleton
{
    private static final Singleton instance = new Singleton();

    public static void main(String[] args)
    {
        Singleton singleton = Singleton.getInstance();

        System.out.println(singleton);
    }

    private Singleton() {};

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

    public String toString() { return Singleton.class.getName(); }
}

我看不出禁止调用私有构造函数的意义。

【讨论】:

  • 但是每个类都至少有一个构造函数,而且似乎问题是在类内部限制对它的调用。
  • 是的,但是如果您阅读我的回答,您会意识到这个问题是多么荒谬。您的反对票是没有根据的。
  • 构造函数还是需要写的,因为如果没有构造函数,编译器会加一个public的,这样就更惨了。
  • 不,不会,如果私有构造函数是默认值,则不会。试试看。
  • 这是真的,但它不会阻止任何人向您的类添加一个创建新实例或更改 get 实例以调用构造函数的方法(这是我认为 OP 害怕的)。我同意你的观点,试图阻止内部的任何人调用构造函数是没有意义的。但是您的示例与您在帖子开头所说的相矛盾:您不需要编写私有构造函数。
【解决方案3】:

官方的回答是否定的。如果有人可以私下访问您的课程,他可以实例化它。

但是你可以这样做:

private static int count = 0;

private MyClass(){
    if(++count>1)throw new IllegalStateException(); //allow exactly one instantiation
}

【讨论】:

  • 正确的想法,但这需要同步才能防弹。如果采用这种方式,您可能希望为此使用 CountDownLatch。
  • 不知道该说什么 duffymo,在重新阅读 OP 之后,这是这里最正确的答案(真的,为了匹配 OP 的请求,它应该总是抛出一个异常,但这显示了如何您仍然可以在允许一个单例实例的同​​时强制执行此条件,这是一种更现实的情况。)如果您认为这个问题是无稽之谈,那是对问题的评论,而不是答案。但这个问题确实有答案。
  • duffymo:虽然您的回答在质疑问题方面更有意义,但我的回答更有意义。请再次说明问题所在以及原因。
【解决方案4】:

不,没有办法强制执行特定类型的单个实例。正如您所描述的,您最接近的方法是将构造函数设为私有,以便只有该类中的函数才能调用它。你可以在运行时做一些事情(比如如果你的静态实例已经存在就抛出一个异常),但这不会在编译时得到任何东西。

【讨论】:

    【解决方案5】:

    如果你正在制作单例,枚举是最好的方法:

    public enum MySingleton {
        INSTANCE;
        // add methods & stuff here
    }
    
    // Usage:
    MySingleton.INSTANCE.doSomething();
    

    “单调性”由 JVM 保证 - 只有一个实例。

    【讨论】:

      【解决方案6】:

      是的,有一个简单的方法:只要让私有构造函数抛出异常即可:

      class MyStaticFunctions {
          private MyStaticFunctions() {
              throw new UnsupportedOperationException("do not instantiate");
          }
      }
      

      请注意,我的答案不同,因为我对问题的解释与列出的大多数其他答案略有不同。许多其他答案假设您希望该类成为单例,在这种情况下,构造函数需要仅调用一次。我假设你不想要类的 any 实例,所以构造函数应该被调用 0 次。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多