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

Posted

技术标签:

【中文标题】如何访问类的私有构造函数?【英文标题】:How can I access a private constructor of a class? 【发布时间】:2011-02-05 15:59:12 【问题描述】:

我是一名 Java 开发人员。在一次采访中,有人问我一个关于私有构造函数的问题:

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

我回答“不”但错了。

你能解释一下我为什么错了,并举一个用私有构造函数实例化对象的例子吗?

【问题讨论】:

对于Java,大多数类似的面试问题都可以回答“是的,你几乎可以做任何事情,但你应该吗?!一般来说,不!”就个人而言,我认为这是一个愚蠢的问题。我不希望我的开发人员这样做,所以我不在乎他们是否知道。更实用的语言细节应该更重要。了解反射的更一般用途可能就足够了。理解 OO 设计模式和语言陷阱比晦涩难懂、应该避免的语言结构更重要。 @nicerobot,我同意你的观点,有时这种技术会破坏目的的实际含义 允许使用反射类访问私有构造函数是一个好习惯吗? 只有私有构造函数的类不能从外部实例化。这可以用于仅具有静态支持方法的类。 (当然你可以使用反射,但是对于这样的类来说,重点是什么。) 今天有人问我这个问题,但在 C# 中,和你一样,我回答“否”,但访问私有构造函数的一种方法是使用返回构造函数的 public static 方法,瞧!现在你有一个私有构造函数的实例存储在任何变量中(在我的例子中是var),而不使用反射。 【参考方案1】: 您可以在类本身中访问它(例如,在公共静态工厂方法中) 如果是嵌套类,可以从封闭类访问 在获得相应权限的前提下,您可以通过反射访问它

目前还不清楚这些是否适用 - 你能提供更多信息吗?

【讨论】:

我使用了你提到的第二种方法(虽然不是故意的:))但似乎它在 C# 中不起作用。我说的对吗? @alireza:是的,C# 有不同的规则。 If it's a nested class, you can access it from the enclosing class 有什么例子吗? @YoushaAleayoub:我相信我会花大约与您一样的时间来想出一个例子:1)创建一个***类。 2)在其中创建一个嵌套类,带有一个私有构造函数。 3) 从***类中的方法调用私有构造函数。你试过了吗? 另外,如果它是一个封闭类,您可以从嵌套类中访问它。【参考方案2】:

这可以通过反射来实现。

考虑一个带有私有构造函数的类 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]);

【讨论】:

【参考方案3】:

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

【讨论】:

【参考方案4】:

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

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? @maryanne 它只是为了使结果在输出中更加明显。没必要。【参考方案5】:

看看单例模式。它使用私有构造函数。

【讨论】:

Singleton 使用私有构造器,在类之外我们不会被实例化,但是我们使用已经实例化的类【参考方案6】:

好吧,如果有任何其他公共构造函数,您也可以。仅仅因为无参数构造函数是私有的,并不意味着您不能实例化该类。

【讨论】:

【参考方案7】:

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

我们可以在类中使用私有构造函数吗?

有时候选人给出的答案是,不,我们不能有私有构造函数。

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

没什么特别的,试着这样想,

私有的:任何私有的东西都只能从类中访问。

构造函数:与类同名的方法,在创建类对象时隐式调用。

或者你可以说,要创建一个对象,你需要调用它的构造函数,如果没有调用构造函数,那么对象就不能被实例化。

这意味着,如果我们在一个类中有一个私有构造函数,那么它的对象只能在该类中实例化。所以用更简单的话你可以说,如果构造函数是私有的,那么你将无法在类之外创建它的对象。

有什么好处 可以实现这个概念来实现单例对象(也就是说只能创建一个类的对象)。

见以下代码,

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 好吧,我想当你去参加 Java 开发人员的面试时,他们可能会问你特定语言(至少是 Java*)的问题。 Can you access a private constructor of a class and instantiate it? 是的,使用反射。没有任何价值的无用问题结束。 这个答案是错误的。您可以通过反射访问类的私有构造函数,从而可以实例化它。但是,如果在构造函数内部,要 ~ ~ 抛出 new UnsupportedOperationException() ~ ~ ~ 可能会覆盖反射并使其几乎不可能(如果不是不可能)实例化抛出异常,尽管可以处理, 取消实例化。 对不起,我的评论有误。最初的答案实际上是受求职面试的启发。抱歉,我刚刚看了标题...【参考方案8】:

是的,正如@Jon Steet 所说,您可以。

访问私有构造函数的另一种方法是在此类中创建一个公共静态方法,并将其返回类型作为其对象。

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中遇到了诸如“反射”之类的新事物。但最后他们期待的简单实现就是这样简单!!!希望这对大家有帮助:) 如果这是一个公认的答案,那么这个问题就会产生误导。这不会访问定义它的类之外的构造函数,它只访问由私有构造函数构造的对象。【参考方案9】:

您可以在课堂外访问它,非常容易访问 仅以 singaltan 类为例,我们都做同样的事情,创建私有构造函数并通过静态方法访问实例这里是与您的查询相关联的代码

ClassWithPrivateConstructor.getObj().printsomething();

它肯定会工作,因为我已经测试过

【讨论】:

那不访问构造函数,只访问构造的对象。 这不会访问构造函数【参考方案10】:

使用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();
      
 

【讨论】:

【参考方案11】:

是的,您可以使用 Reflection 使用私有构造函数实例化一个实例,请参阅下面从 java2s 粘贴的示例以了解如何:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Deny 
  private Deny() 
    System.out.format("Deny constructor%n");
  


public class ConstructorTroubleAccess 
  public static void main(String... args) 
    try 
      Constructor c = Deny.class.getDeclaredConstructor();
      // c.setAccessible(true); // solution
      c.newInstance();

      // production code should handle these exceptions more gracefully
     catch (InvocationTargetException x) 
      x.printStackTrace();
     catch (NoSuchMethodException x) 
      x.printStackTrace();
     catch (InstantiationException x) 
      x.printStackTrace();
     catch (IllegalAccessException x) 
      x.printStackTrace();
    
  

【讨论】:

先setAccessible为true,然后调用newInstance()。 c.setAccessible(true); c.newInstance();【参考方案12】:

我喜欢上面的答案,但是还有另外两种漂亮的方法可以创建具有私有构造函数的类的新实例。这完全取决于您想要达到什么目标以及在什么情况下。

1:使用Java instrumentation 和ASM

在这种情况下,您必须使用转换器启动 JVM。为此,您必须实现一个新的 Java 代理,然后让这个转换器为您更改构造函数。

首先创建class transformer。这个类有一个叫做transform的方法。重写此方法,在此方法中,您可以使用 ASM class reader 和其他类来操作构造函数的可见性。转换器完成后,您的客户端代码将可以访问构造函数。

您可以在此处阅读更多信息:Changing a private Java constructor with ASM

2:重写构造函数代码

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

后一种更改称为link seam,这是一种接缝,其中启用点是类路径。

【讨论】:

喜欢这个想法,但从技术上讲,这些绕过涉及的代码更改比编写新代码更多。【参考方案13】:

拥有私有构造函数的基本前提是拥有私有构造函数会限制对自己类的代码以外的代码的访问,使其无法生成该类的对象。

是的,我们可以在一个类中拥有私有构造函数,是的,可以通过创建一些静态方法来访问它们,这些静态方法反过来为该类创建新对象。

 Class A
private A()

private static createObj()
return new A();


Class B
public static void main(String[]args)
A a=A.createObj();

所以要创建这个类的对象,其他类必须使用静态方法。

当我们将构造函数设为私有时,使用静态方法有什么意义?

存在静态方法,因此如果需要创建该类的实例,则可以在创建实例之前在静态方法中应用一些预定义的检查。例如,在 Singleton 类中,静态方法检查实例是否已经创建。如果实例已经创建,那么它只是简单地返回该实例而不是创建一个新实例。

 public static MySingleTon getInstance()
    if(myObj == null)
        myObj = new MySingleTon();
    
    return myObj;

【讨论】:

【参考方案14】:

是的,我们可以访问私有构造函数或使用私有构造函数实例化一个类。 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

我希望你明白了。

【讨论】:

【参考方案15】:

我希望这个例子可以帮助你:

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();
       
    


输出: 调用私有构造函数

【讨论】:

【参考方案16】:

我们不能在类之外访问私有构造函数,但是使用 Java 反射 API 我们可以访问私有构造函数。请找到以下代码:

public class Test
    private Test()
    System.out.println("Private Constructor called");




public class PrivateConsTest
    public void accessPrivateCons(Test test)

        Field[] fields = test.getClass().getDeclaredFields();

        for (Field field : fields) 
            if (Modifier.isPrivate(field.getModifiers())) 
                field.setAccessible(true);
                System.out.println(field.getName()+" : "+field.get(test));
            
        
    

如果您使用 Spring IoC,Spring 容器还会创建并注入具有私有构造函数的类的对象。

【讨论】:

【参考方案17】:

我试过这样它正在工作。如果我错了,请给我一些建议。

import java.lang.reflect.Constructor;

class TestCon 
private TestCon() 
    System.out.println("default constructor....");


public void testMethod() 
    System.out.println("method executed.");
  


class TestPrivateConstructor 

public static void main(String[] args) 
    try 
        Class testConClass = TestCon.class;
        System.out.println(testConClass.getSimpleName());

        Constructor[] constructors =    testConClass.getDeclaredConstructors();
        constructors[0].setAccessible(true);
        TestCon testObj = (TestCon) constructors[0].newInstance();
        //we can call method also..
        testObj.testMethod();
     catch (Exception e) 
        e.printStackTrace();
    



【讨论】:

【参考方案18】:

简单的答案是肯定的,我们可以在 Java 中使用私有构造函数。

我们可以在多种场景中使用私有构造函数。主要的是

内部构造函数链接 单例类设计模式

【讨论】:

【参考方案19】:

Reflection 是 java 中的一个 API,我们可以使用它在运行时调用方法,而不管它们使用的访问说明符如何。 访问类的私有构造函数:

My utility class

public final class Example
    private Example()
        throw new UnsupportedOperationException("It is a utility call");
    
    public static int twice(int i)
    
        int val = i*2;
        return val;
    

My Test class which creates an object of the Utility class(Example)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
class Test
    public static void main(String[] args) throws Exception 
        int i =2;
        final Constructor<?>[] constructors = Example.class.getDeclaredConstructors();
        constructors[0].setAccessible(true);
        constructors[0].newInstance();
    

调用构造函数时会报错 java.lang.UnsupportedOperationException: It is a utility call

但请记住使用反射 api 会导致开销问题

【讨论】:

以上是关于如何访问类的私有构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

java内部私有类的构造函数

在类的构造函数上使用受保护的访问修饰符

指向具有私有构造函数的类的类成员的指针

类的私有private构造函数 ,为什么要这样做

在私有继承中公开构造函数

带参数的Java私有构造函数[重复]