2010-04-08 52 views
40

Soy un desarrollador de Java. En una entrevista, me hicieron una pregunta sobre constructores privados:¿Cómo puedo acceder a un constructor privado de una clase?

¿Se puede acceder a un constructor privado de una clase y crear una instancia?

Respondí 'No', pero estaba equivocado.

¿Puede explicar por qué me equivoqué y dar un ejemplo de crear instancias de un objeto con un constructor privado?

+11

Con respecto a Java, preguntas de la entrevista más similares se pueden contestar "Sí, se puede hacer prácticamente nada, pero en caso de que ?! general, no!" Personalmente, creo que es una pregunta estúpida. No quiero que mis desarrolladores lo hagan, así que no me importa si lo saben. Detalles de lenguaje más prácticos deberían importar mucho más. Conocer la utilidad más general de la reflexión es probablemente suficiente. Comprender los patrones de diseño de OO y los errores en el lenguaje es mucho más importante que los constructos de lenguaje oscuros que deberían evitarse. – nicerobot

+0

@nicerobot, estoy de acuerdo con usted, algunas veces estas técnicas derrotan el significado real del propósito – gmhk

+0

¿es una buena práctica permitir el acceso al constructor privado utilizando la clase de reflexión? – gmhk

Respuesta

50
  • Puede acceder a ella dentro de la clase en sí (por ejemplo, en un método de fábrica estática pública)
  • Si se trata de una clase anidada, se puede acceder a él desde la clase envolvente
  • Sujeto a permisos apropiados, puede acceda a ella con la reflexión

No obstante, no está claro si se aplica alguna de estas: ¿puede proporcionar más información?

+0

Utilicé el segundo método que mencionaste (aunque sin intención :)) pero parece que no funciona en C#. ¿Estoy en lo cierto? –

+0

@alireza: Sí, C# tiene reglas diferentes. –

+0

'Si se trata de una clase anidada, puede acceder a ella desde la clase adjunta' ¿Algún ejemplo? –

15

Esto se puede lograr utilizando la reflexión.

tener en cuenta para una prueba de clase, con un constructor privado:

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]); 
+0

"Reflexividad" corregida -> "reflexión". – sleske

+1

Sí, lo he comprobado, funciona, gracias – gmhk

1

Puede, por supuesto, el acceso constructor privado de otros métodos o constructores de la misma clase y sus clases internas. Usando la reflexión, también puede usar el constructor privado en otro lugar, siempre que SecurityManager no lo impida.

55

Una forma de eludir la restricción es utilizar reflexiones:

import java.lang.reflect.Constructor; 

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

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

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

y ¿por qué necesitamos el último método aquí, anulado toString? – annabretsko

+0

@maryanne está ahí para hacer que el resultado sea más obvio en la salida. No es necesario. –

+0

'new Class [0]' y 'new Object [0]' son innecesarios. – shmosel

1

Mire el patrón Singleton. Utiliza constructor privado.

+1

Singleton usa el contructor privado, fuera de la clase no seremos instanciados, pero usamos la clase ya instanciada – gmhk

0

Bueno, también puedes hacerlo si hay otros constructores públicos. El hecho de que el constructor sin parámetros sea privado no significa que no puedas crear una instancia de la clase.

5

La primera pregunta que se hace en relación con constructores privados en entrevistas es,

podemos tener constructor privado en una clase?

Y a veces la respuesta dada por el candidato es: No, no podemos tener constructores privados.

Así que me gustaría decir, Sí, puede tener constructores privados en una clase.

No es ninguna cosa especial, trata de pensar de esta manera,

Privado: nada privada se puede acceder desde dentro de la única clase.

Constructor: método que tiene el mismo nombre que el de clase y se invoca implícitamente cuando se crea el objeto de la clase.

o puede decir, para crear un objeto que necesita llamar a su constructor, si no se llama al constructor, no se puede crear una instancia del objeto.

Significa que, si tenemos un constructor privado en una clase, entonces sus objetos solo se pueden instanciar dentro de la clase. Entonces, en palabras más simples, puede decir que si el constructor es privado, no podrá crear sus objetos fuera de la clase.

Cuál es el beneficio Este concepto se puede aplicar para alcanzar objeto singleton (que significa sólo un objeto de la clase puede ser creado).

Véase el siguiente código,

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. 

    } 
} 
+0

¿Por qué alguien haría una pregunta tan delicada y específica del idioma en una entrevista? – dfeuer

+1

@dfeuer Bueno, creo que cuando vayas a la entrevista para el desarrollador de Java, es posible que te hagan preguntas específicas del idioma (Java al menos *). – gprathour

3

Sí se podía, como se ha mencionado por @ Jon Steet.

Otra forma de acceder a un constructor privado es crear un método público estático dentro de esta clase y tener su tipo de devolución como su objeto.

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

Me encontré con la misma pregunta y encontré varias soluciones mencionadas por @Jon Steet. También se encontró con cosas nuevas como "Reflection" en Java también al usar métodos de fábrica. Pero finalmente la simple implementación que esperaban era simple como esta !!! Espero que esto ayude chicos :) – Tapeshvar

+0

Si esta es una respuesta aceptada, entonces la pregunta es engañosa. Esto no accede al constructor fuera de la clase en la que está definido, solo accede a un objeto construido por el constructor privado. – Ray

-1

puede acceder a él fuera de la clase que es muy fácil acceder a la tomar sólo un ejemplo de la clase singaltan todos hace lo mismo que hacemos el constructor privado y acceder a la instancia por el método estático que aquí se asocia el código a su consulta

ClassWithPrivateConstructor.getObj().printsomething(); 

que funcionará sin duda porque ya he probado

+0

Eso no accede al constructor, solo al objeto construido. – Ray

3

utilizando la reflexión de java de la siguiente manera:

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

Sí se puede crear una instancia con un constructor privado utilizando Reflection, véase el ejemplo que pegado a continuación quitado de java2s entender cómo:

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

Primer setAccessible como verdadero y luego llame a newInstance(). c.setAccessible (verdadero); c.newInstance(); – mahesh

1

me gustan las respuestas anteriores, pero hay dos maneras más ingeniosas de crear una nueva instancia de una clase que tiene un constructor privado. Todo depende de lo que quiere lograr y bajo qué circunstancias.

1: Uso de Java instrumentation y ASM

Bueno, en este caso hay que empezar la JVM con un transformador. Para hacer esto, debe implementar un nuevo agente Java y luego hacer que este transformador cambie el constructor por usted.

Primero cree el class transformer. Esta clase tiene un método llamado transformación. Reemplace este método y dentro de este método puede usar el ASM class reader y otras clases para manipular la visibilidad de su constructor. Una vez hecho el transformador, su código de cliente tendrá acceso al constructor.

Puede leer más sobre esto aquí: Changing a private Java constructor with ASM

2: Vuelva a escribir el código de constructor

Bueno, esto no es realmente accediendo al constructor, pero aún así se puede crear una instancia.Supongamos que usa una biblioteca de terceros (digamos Guava) y tiene acceso al código, pero no desea cambiar ese código en el contenedor que la JVM carga por alguna razón (lo sé, esto es no muy real pero supongamos que el código está en un contenedor compartido como Jetty y no puedes cambiar el código compartido, pero tienes un contexto de carga de clases separado) entonces puedes hacer una copia del código de terceros con el constructor privado, cambiar el constructor privado para proteger o publicar en tu código y luego poner tu clase al comienzo de la ruta de clase. A partir de ese momento, su cliente puede usar el constructor modificado y crear instancias.

Este último cambio se llama link seam, que es un tipo de costura donde el punto de habilitación es el classpath.

0

La premisa básica para tener un constructor privado es que tener un constructor privado restringe el acceso de código que no sea el código de la clase propia para crear objetos de esa clase.

Sí, podemos tener constructores privados en una clase y sí, pueden hacerse accesibles haciendo algunos métodos estáticos que a su vez crean el nuevo objeto para la clase.

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

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

Para hacer un objeto de esta clase, la otra clase tiene que usar los métodos estáticos.

¿Qué sentido tiene tener un método estático cuando hacemos que el constructor sea privado?

Existen métodos estáticos, de modo que en caso de que sea necesario realizar la instancia de esa clase, puede haber algunas comprobaciones predefinidas que se pueden aplicar en los métodos estáticos antes de la creación de la instancia. Por ejemplo, en una clase Singleton, el método estático comprueba si la instancia ya se ha creado o no. Si la instancia ya está creada, simplemente devuelve esa instancia en lugar de crear una nueva.

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

Sí, podemos acceder al constructor privado o instanciar una clase con constructor privado. La API de reflexión de Java y el patrón de diseño singleton han utilizado el concepto para acceder al constructor privado. Además, los contenedores de marco de primavera pueden acceder al constructor privado de beans y este marco ha utilizado la API de reflexión de java. El siguiente código muestra la forma de acceder al constructor privado.

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[] = class.getDeclaredConstructor(); 
      con[0].setAccessible(true); 
      con[0].newInstance(null); 
     }catch(Exception e){} 

    } 
} 

output: 
private constructor invocation 

Espero que lo hayas conseguido.

0

Para acceder al constructor privado de la clase, Java proporciona reflexión. Puede hacerlo con la ayuda de java.lang.Class y java.lang.refelect.Constructor.

Uso getDeclaredConstructor() método de java.lang.Class. Volverá a generar la matriz del objeto constructor.

package org.websparrow.access; 

public class Honda { 

// private constructor 
private Honda(){ 
    System.out.println("I am private constructor of Honda class."); 
    } 
} 

Crea una instantánea de la clase de Honda en la clase de automóvil.

package org.websparrow.access; 

import java.lang.reflect.Constructor; 

public class Car { 
public static void main(String[] args) { 
    try { 

     Class<?> cls = Class.forName("org.websparrow.access.Honda"); 

     Constructor<?>[] cons = cls.getDeclaredConstructors(); 
     cons[0].setAccessible(true); 
     cons[0].newInstance(); 

    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 
} 

Para más detalles puede tomar forma referance How to access private fields, methods and constructors of a class in Java

Cuestiones relacionadas