web-dev-qa-db-de.com

Wie kann ich auf einen privaten Konstruktor einer Klasse zugreifen?

Ich bin ein Java-Entwickler. In einem Interview wurde mir eine Frage zu privaten Konstrukteuren gestellt:

Können Sie auf einen privaten Konstruktor einer Klasse zugreifen und diesen instanziieren?

Ich antwortete 'Nein', war aber falsch.

Können Sie erklären, warum ich mich geirrt habe, und ein Beispiel für die Instanziierung eines Objekts mit einem privaten Konstruktor geben?

58
gmhk

Eine Möglichkeit, die Einschränkung zu umgehen, ist die Verwendung von Reflexionen:

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!";
    }
}
75
Joachim Sauer
  • Sie können auf die Klasse selbst zugreifen (z. B. in einer öffentlichen statischen Factory-Methode).
  • Wenn es sich um eine verschachtelte Klasse handelt, können Sie über die umgebende Klasse darauf zugreifen
  • Abhängig von den entsprechenden Berechtigungen können Sie mit Reflektion darauf zugreifen

Es ist nicht wirklich klar, ob eine dieser Bedingungen zutrifft. Können Sie weitere Informationen geben?

67
Jon Skeet

Dies kann durch Reflexion erreicht werden.

Betrachten Sie eine Klasse Test mit einem privaten Konstruktor:

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]);
19
tonio

Die allererste Frage, die in privaten Interviews zu privaten Konstrukteuren gestellt wird, lautet:

Können wir einen privaten Konstruktor in einer Klasse haben?

Und manchmal lautet die Antwort des Kandidaten: Nein, wir können keine privaten Konstrukteure haben.

Also würde ich gerne sagen: Ja, Sie können private Konstruktoren in einer Klasse haben.

Es ist keine besondere Sache, versuchen Sie es so zu denken,

Privatgelände: Auf alles Private kann nur innerhalb der Klasse zugegriffen werden.

Konstrukteur: Eine Methode, die denselben Namen wie die Klasse hat und implizit aufgerufen wird, wenn ein Objekt der Klasse erstellt wird.

oder Sie können sagen, um ein Objekt zu erstellen, müssen Sie seinen Konstruktor aufrufen. Wenn der Konstruktor nicht aufgerufen wird, kann das Objekt nicht instanziiert werden.

Das heißt, wenn wir einen privaten Konstruktor in einer Klasse haben, können seine Objekte nur innerhalb der Klasse instanziiert werden. In einfacheren Worten können Sie also sagen: Wenn der Konstruktor privat ist, können Sie seine Objekte nicht außerhalb der Klasse erstellen.

Was ist der Nutzen?Dieses Konzept kann umgesetzt werden singleton-Objekt (es kann nur ein Objekt der Klasse erstellt werden).

Siehe den folgenden Code,

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.

    }
}
6
gprathour

Java Reflection wie folgt verwenden:

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

Ja, das könnte man, wie von @Jon Steet erwähnt.

Eine andere Möglichkeit, auf einen privaten Konstruktor zuzugreifen, besteht darin, eine öffentliche statische Methode innerhalb dieser Klasse zu erstellen, deren Rückgabetyp das Objekt ist.

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
Tapeshvar

Auf den privaten Konstruktor können Sie natürlich auch von anderen Methoden oder Konstruktoren derselben Klasse und deren inneren Klassen zugreifen. Mit Reflection können Sie den privaten Konstruktor auch an anderer Stelle verwenden, vorausgesetzt der SecurityManager hindert Sie nicht daran.

2
jarnbjo

Ich mag die obigen Antworten, aber es gibt zwei weitere Möglichkeiten, eine neue Instanz einer Klasse mit privatem Konstruktor zu erstellen. Es hängt alles davon ab, was Sie unter welchen Umständen erreichen möchten.

1: Verwenden von Java-Instrumentierung und ASM

In diesem Fall müssen Sie die JVM mit einem Transformator starten. Dazu müssen Sie einen neuen Java-Agenten implementieren und dann den Konstruktor für diesen Transformator ändern.

Erstellen Sie zuerst den Transformator class . Diese Klasse hat eine Methode namens transform. Überschreiben Sie diese Methode, und innerhalb dieser Methode können Sie den ASM class-Reader und andere Klassen verwenden, um die Sichtbarkeit Ihres Konstruktors zu ändern. Nachdem der Transformer fertig ist, hat Ihr Clientcode Zugriff auf den Konstruktor.

Mehr dazu lesen Sie hier: Ändern eines privaten Java-Konstruktors mit ASM

2: Schreiben Sie den Konstruktorcode neu

Nun, das ist nicht wirklich Zugriff auf den Konstruktor, aber Sie können immer noch eine Instanz erstellen. Nehmen wir an, Sie verwenden eine Bibliothek eines Drittanbieters (beispielsweise Guava) und haben Zugriff auf den Code. Sie möchten jedoch nicht den Code in der JAR-Datei ändern, die von der JVM aus irgendeinem Grund geladen wird (ich weiß, das ist nicht sehr naturgetreu, aber nehmen wir an, der Code befindet sich in einem gemeinsam genutzten Container wie Jetty und Sie können den gemeinsam genutzten Code nicht ändern, Sie verfügen jedoch über einen separaten Klassenladekontext Der private Konstruktor wird in Ihrem Code geschützt oder als public deklariert und setzt dann Ihre Klasse an den Anfang des Klassenpfads. Ab diesem Zeitpunkt kann Ihr Client den geänderten Konstruktor verwenden und Instanzen erstellen.

Diese letzte Änderung wird als link seam bezeichnet. Hierbei handelt es sich um eine Art Naht, bei der der Aktivierungspunkt der Klassenpfad ist.

2
Captain Fogetti

Ja, wir können auf den privaten Konstruktor zugreifen oder eine Klasse mit privatem Konstruktor instanziieren. Die Java Reflection-API und das Singleton-Entwurfsmuster haben das Konzept für den Zugriff auf den privaten Konstruktor ..__ stark genutzt. Spring-Framework-Container können auch auf den privaten Konstruktor von Beans zugreifen, und dieses Framework hat die Java-Reflection-API verwendet die Art des Zugriffs auf den privaten Konstruktor. 

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

Ich hoffe du hast es verstanden.

2
Sushan Baskota

Schauen Sie sich das Singleton-Muster an. Es verwendet privaten Konstruktor.

0
3biga

Ich hoffe, dieses Beispiel kann Ihnen helfen:

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

}

Ausgabe: Privater Konstruktor aufgerufen

0
Niravdas

Wir können nicht auf einen privaten Konstruktor außerhalb der Klasse zugreifen. Mit der Java Reflection-API können wir jedoch auf den privaten Konstruktor zugreifen. Bitte finden Sie unter dem Code:

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

Wenn Sie Spring IoC verwenden, erstellt der Spring-Container auch Objekte der Klasse mit privatem Konstruktor und fügt sie ein. 

0

Einfache Antwort ist ja, wir können private Konstruktoren in Java haben.

Es gibt verschiedene Szenarien, in denen private Konstruktoren verwendet werden können. Die wichtigsten sind

  • Verkettung des internen Konstruktors 
  • Singleton-Klassenentwurfsmuster
0
CodeRider

Ich habe so versucht, dass es funktioniert. Gib mir einen Vorschlag, wenn ich falsch liege.

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

Ja, Sie können eine Instanz mit einem privaten Konstruktor mit Reflection instanziieren. Siehe das Beispiel, das ich unten in Java2s eingefügt habe, um zu verstehen, wie:

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
madx

Reflection ist eine API in Java, mit der wir Methoden zur Laufzeit aufrufen können, unabhängig vom verwendeten Zugriffsspezifizierer. So greifen Sie auf einen privaten Konstruktor einer Klasse zu:

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

Beim Aufruf des Konstruktors wird der Fehler Java.lang.UnsupportedOperationException: It is a utility call ausgegeben

Denken Sie jedoch daran, dass die Verwendung von Reflection API Overhead-Probleme verursacht

0
Animesh Jaiswal

Die Grundvoraussetzung für einen privaten Konstruktor ist, dass ein privater Konstruktor den Zugriff auf anderen Code als den eigenen Klassencode daran hindert, Objekte dieser Klasse zu erstellen.

Ja, wir können private Konstruktoren in einer Klasse haben. Ja, sie können durch statische Methoden zugänglich gemacht werden, die wiederum das neue Objekt für die Klasse erstellen.

 Class A{
private A(){
}
private static createObj(){
return new A();
}

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

Um ein Objekt dieser Klasse zu erstellen, muss die andere Klasse die statischen Methoden verwenden.

Was ist der Sinn einer statischen Methode, wenn wir den Konstruktor privat machen?

Statische Methoden sind vorhanden, so dass für den Fall, dass die Instanz dieser Klasse erstellt werden muss, einige vordefinierte Prüfungen vorhanden sein können, die vor dem Erstellen der Instanz in den statischen Methoden angewendet werden können. In einer Singleton-Klasse prüft die statische Methode beispielsweise, ob die Instanz bereits erstellt wurde oder nicht. Wenn die Instanz bereits erstellt wurde, wird diese Instanz einfach zurückgegeben, anstatt eine neue Instanz zu erstellen.

 public static MySingleTon getInstance(){
    if(myObj == null){
        myObj = new MySingleTon();
    }
    return myObj;
}
0
Nikhil Arora