如何访问类的私有构造函数?

84

我是一名Java开发人员。在面试中,我被问到一个关于私有构造函数的问题:

你能访问类的私有构造函数并实例化它吗?

我回答'不可以',但是我的答案是错误的。

你能解释一下我错在哪里,并给出一个使用私有构造函数实例化对象的例子吗?


25
关于Java,大多数类似的面试问题可以回答:“是的,你几乎可以做任何事情,但你应该吗?一般来说,不应该!”就我个人而言,我认为这是一个愚蠢的问题。我不希望我的开发人员去做那些事情,所以我不在意他们是否知道如何做。更实际的语言细节应该更重要。了解反射的更一般的实用性可能已经足够了。理解面向对象设计模式和语言上的限制比那些应该避免的晦涩语言结构更重要。 - nicerobot
一个只有私有构造函数的类无法从外部实例化。这可以用于仅具有静态支持方法的类。(当然,您可以使用反射,但对于这样的类,这有什么意义呢。) - Daniel
今天我被问到了类似的问题,只不过是关于C#语言的。和你一样,我的回答也是“不行”,但是有一种方法可以访问私有构造函数,那就是使用一个公共静态方法来返回该构造函数,然后就可以在任何变量中(如我的var)存储私有构造函数的实例,而无需使用反射技术。 - ArCiGo
阅读此链接,我认为会对您有所帮助:https://dev59.com/A3M_5IYBdhLWcg3w2XPw - Fox
@Daniel Singleton除了getinstance之外,不需要静态方法。然后是在同一类中对实例的静态引用。其他所有内容都不需要是静态的。你所描述的是一个实用程序。 - Potato
显示剩余5条评论
21个回答

110

绕过限制的一种方法是使用反射:

import java.lang.reflect.Constructor;

public class Example {
    public static void main(final String[] args) throws Exception {
        Constructor<Foo> constructor = Foo.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Foo foo = constructor.newInstance();
        System.out.println(foo);
    }
}

class Foo {
    private Foo() {
        // private!
    }

    @Override
    public String toString() {
        return "I'm a Foo and I'm alright!";
    }
}

我们为什么需要在这里使用重写的toString方法? - user6123563
3
@maryanne 这只是为了让输出结果更明显,而不是必要的。 - Joachim Sauer

76
  • 您可以在类本身内部访问它(例如,在公共静态工厂方法中)
  • 如果它是嵌套类,则可以从封闭类中访问它
  • 在适当的权限下,您可以使用反射访问它

但并不清楚这些是否适用 - 您能提供更多信息吗?


我使用了你提到的第二种方法(虽然并非有意为之 :)),但似乎在C#中不起作用。我是对的吗? - Alireza Farahani
@alireza:是的,C#有不同的规则。 - Jon Skeet
如果它是一个嵌套类,你可以从封闭类中访问它。有任何例子吗? - Yousha Aleayoub
1
@YoushaAleayoub:我相信我需要和你一样的时间来举一个例子:1)创建一个顶层类。2)在其中创建一个嵌套类,并使用私有构造函数。3)从顶层类中的方法调用私有构造函数。你试过这个方法吗? - Jon Skeet
1
此外,如果它是一个封闭类,你可以从嵌套类中访问它。 - jdarthenay

24

可以使用反射来实现这一点。

考虑一个具有私有构造函数的Test类:

Constructor<?> constructor  = Test.class.getDeclaredConstructor(Context.class, String[].class);
Assert.assertTrue(Modifier.isPrivate(constructor.getModifiers()));
constructor.setAccessible(true);
Object instance = constructor.newInstance(context, (Object)new String[0]);

11

在面试中关于私有构造函数被问到的第一个问题是:

一个类中能否有私有构造函数?

而有时候应聘者给出的答案是:不,我们不能有私有构造函数。

所以我想说:是的,你可以在一个类中拥有私有构造函数。

这并没有什么特别的,试着这样思考一下:

私有:任何私有内容只能从类内部访问。

构造函数:一个与类名相同的方法,当创建该类的对象时会自动调用。

或者说,要创建一个对象就需要调用它的构造函数,如果没有调用构造函数,则无法实例化对象。

这意味着,如果一个类中有私有构造函数,则其对象只能在类内部实例化。简单地说,如果构造函数是私有的,那么你就无法在类外创建它的对象。

好处是:

这个概念可以用来实现单例对象(也就是只能创建一个类对象)。

看下面的代码:

class MyClass{
    private static MyClass obj = new MyClass();

    private MyClass(){

    }

    public static MyClass getObject(){
        return obj;
    }
}
class Main{
    public static void main(String args[]){

        MyClass o = MyClass.getObject();
        //The above statement will return you the one and only object of MyClass


        //MyClass o = new MyClass();
        //Above statement (if compiled) will throw an error that you cannot access the constructor.

    }
}

现在是棘手的部分,为什么你是错的,就像其他答案已经解释过的那样,您可以使用反射绕过限制。


为什么有人会在面试中问这样一个挑剔的、特定于语言的问题呢? - dfeuer
1
@dfeuer 我认为当你去面试Java开发人员时,他们可能会问你一些特定于语言的问题(至少是Java方面的问题)。 - gprathour
可以使用反射来访问一个类的私有构造函数并实例化它吗?是的。这是一个没有任何价值的问题的结尾。 - Martin Marconcini
这个答案是错误的。你可以通过反射访问一个类的私有构造函数,从而实例化它。然而,如果在构造函数内部使用 throw new UnsupportedOperationException(),则可能会覆盖反射并使其几乎不可能(如果不是不可能)进行实例化,因为异常被抛出,虽然可以处理,但取消了实例化。 - JustAFellowCoder
抱歉,我的评论是错误的。原始答案实际上是由一次面试激发的。抱歉,我只看了标题... - mihca

5
我喜欢上面的答案,但是还有两种更巧妙的方法可以创建一个具有私有构造函数的类的新实例。这完全取决于你想要实现什么以及在什么情况下。
1:使用Java检测工具和ASM
好吧,在这种情况下,您必须使用转换器启动JVM。为此,您必须实现一个新的Java代理,然后使该转换器为您更改构造函数。
首先创建类转换器。该类有一个名为transform的方法。重写此方法并在其中使用ASM类阅读器和其他类来操作您的构造函数的可见性。完成转换器后,您的客户端代码将可以访问构造函数。

您可以在此处阅读更多信息: 使用ASM更改私有Java构造函数

2: 重写构造函数代码

嗯,这并不是真正访问构造函数,但仍然可以创建实例。假设您使用第三方库(比如Guava),您可以访问该代码,但由于某些原因您不想更改JVM加载的jar中的代码(我知道,这不太现实,但假设代码在像Jetty这样的共享容器中,您无法更改共享代码,但您有单独的类加载上下文),那么您可以将带有私有构造函数的第三方代码复制一份,将您代码中的私有构造函数更改为受保护或公共,并将您的类放在类路径的开头。从那时起,您的客户端就可以使用修改后的构造函数并创建实例。

后一种更改称为链接接缝,它是一种启用点为类路径的接缝。


1
喜欢这个想法,但从技术上讲,这些绕过方法涉及更多的代码更改而不是编写新代码。 - JustAFellowCoder

4
使用Java反射的方式如下:
   import java.lang.reflect.Constructor;

   import java.lang.reflect.InvocationTargetException;

   class Test   
   {

      private Test()  //private constructor
      {
      } 
   }

  public class Sample{

      public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException
     {

       Class c=Class.forName("Test"); //specify class name in quotes

       //----Accessing private constructor
       Constructor con=c.getDeclaredConstructor();
       con.setAccessible(true);     
       Object obj=con.newInstance();
   }   
} 

3

正如@Jon Steet所提到的那样,您可以这样做。

访问私有构造函数的另一种方法是在该类中创建一个public static方法,并将其返回类型设置为其对象。

public class ClassToAccess
{

    public static void main(String[] args)
    {
        {
            ClassWithPrivateConstructor obj = ClassWithPrivateConstructor.getObj();
            obj.printsomething();
        }

    }

}

class ClassWithPrivateConstructor
{

    private ClassWithPrivateConstructor()
    {
    }

    public void printsomething()
    {
        System.out.println("HelloWorld");
    }

    public static ClassWithPrivateConstructor getObj()
    {
        return new ClassWithPrivateConstructor();
    }
}

我遇到了同样的问题,并发现了@Jon Steet提到的各种解决方案。同时,我还学习了Java中的“反射”以及使用工厂方法等新内容。但最终他们期望的简单实现就是这么简单!!!希望这能帮到大家 :) - Tapeshvar
1
如果这是一个被接受的答案,那么问题是具有误导性的。这并没有在定义它的类之外访问构造函数,它只访问由私有构造函数构建的对象。 - Ray

2

是的,我们可以访问私有构造函数或实例化具有私有构造函数的类。Java反射API和单例设计模式经常利用这个概念来访问私有构造函数。 另外,Spring框架容器可以访问bean的私有构造函数,并且该框架使用了Java反射API。 以下代码演示了访问私有构造函数的方法。

class Demo{
     private Demo(){
      System.out.println("private constructor invocation");
     }
}

class Main{
   public static void main(String[] args){
       try{
           Class class = Class.forName("Demo");
           Constructor<?> con = string.getDeclaredConstructor();
           con.setAccessible(true);
           con.newInstance(null);
       }catch(Exception e){}

   }
}

output:
private constructor invocation

我希望你能理解这个问题。

2

当然,您可以从同一类及其内部类的其他方法或构造函数中访问私有构造函数。使用反射,您还可以在其他地方使用私有构造函数,前提是SecurityManager没有阻止您这样做。


2
我希望这个例子能够对您有所帮助:
package MyPackage;

import java.lang.reflect.Constructor;

/**
 * @author Niravdas
 */

class ClassWithPrivateConstructor {

    private ClassWithPrivateConstructor() {
        System.out.println("private Constructor Called");
    }

}
public class InvokePrivateConstructor 
{
     public static void main(String[] args) {
        try
        {
           Class ref = Class.forName("MyPackage.ClassWithPrivateConstructor");
           Constructor<?> con = ref.getDeclaredConstructor();
           con.setAccessible(true);
           ClassWithPrivateConstructor obj = (ClassWithPrivateConstructor) con.newInstance(null);
       }catch(Exception e){
           e.printStackTrace();
       }
    }

}

输出: 私有构造函数已调用


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