如何访问类的私有构造函数? Java仪器ASM

问题描述 投票:59回答:18

我是一名Java开发人员。在一次采访中,我被问到一个关于私人建筑师的问题:

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

我回答'不',但错了。

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

java oop constructor instance
18个回答
79
投票

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

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!";
    }
}

0
投票

是的,您可以使用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();
    }
  }
}

0
投票

拥有私有构造函数的基本前提是拥有私有构造函数限制除了自己的类代码之外的代码访问,从而使该类的对象成为可能。

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

 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;
}

0
投票

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

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

}

输出:私有构造函数调用


0
投票

我们无法访问类外的私有构造函数,但使用Java Reflection 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容器还会创建并注入具有私有构造函数的类的对象。


0
投票

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

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

}
}

0
投票

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

在各种情况下我们可以使用私有构造函数。主要是

  • 内部构造函数链接
  • Singleton类设计模式

-1
投票

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


-2
投票

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


-3
投票

你可以在类之外访问它非常容易访问只是举一个singaltan类的例子我们都做同样的事情使私有构造函数和静态方法访问实例这里是与你的查询相关的代码

ClassWithPrivateConstructor.getObj().printsomething();

它肯定会起作用,因为我已经测试过了


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

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


19
投票

这可以使用反射来实现。

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

6
投票

关于访谈中私人建筑商的第一个问题是,

我们可以在一个类中拥有私有构造函数吗?

而有时候候选人给出的答案是,不,我们不能拥有私人建设者。

所以我想说,是的,你可以在课堂上拥有私人建筑师。

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

私人:私人的任何东西都只能在课堂上访问。

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

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

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

有什么好处这个概念可以用来实现单例对象(这意味着只能创建一个类的对象)。

请参阅以下代码,

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.

    }
}

现在是棘手的部分,为什么你错了,正如在其他答案中已经解释的那样,你可以使用Reflection绕过限制。


4
投票

使用java Reflection如下:

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

3
投票

我喜欢上面的答案,但是有两种更好的方法可以创建一个具有私有构造函数的类的新实例。这一切都取决于你想要达到的目标以及在什么情况下。

1: Using Java instrumentation and ASM

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

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

你可以在这里阅读更多相关信息:Changing a private Java constructor with ASM

2: Rewrite the constructor code

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

后一种变化称为link seam,它是一种接缝,其中使能点是类路径。


2
投票

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


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

我希望你明白了。

© www.soinside.com 2019 - 2024. All rights reserved.