Je suis un développeur Java. Dans une interview, on m'a posé une question sur les constructeurs privés:
Pouvez-vous accéder à un constructeur privé d'une classe et l'instancier?
J'ai répondu «non» mais je me suis trompé.
Pouvez-vous expliquer pourquoi je me suis trompé et donner un exemple d'instanciation d'un objet avec un constructeur privé?
Une façon de contourner la restriction consiste à utiliser des réflexions:
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!";
}
}
Ce n'est pas vraiment clair si l'un de ces cas s'applique bien - pouvez-vous donner plus d'informations?
Ceci peut être réalisé en utilisant la réflexion.
Considérons un test de classe, avec un constructeur privé:
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]);
La toute première question posée concernant les constructeurs privés dans les entretiens est la suivante:
Peut-on avoir un constructeur privé dans une classe?
Et parfois, la réponse du candidat est: Non, nous ne pouvons pas avoir de constructeurs privés.
Je voudrais donc dire: Oui, vous pouvez avoir des constructeurs privés dans une classe.
Ce n'est pas spécial, essayez de penser comme ça,
Privé: tout ce qui est privé ne peut être consulté que dans la classe.
Constructeur: une méthode qui porte le même nom que celle de classe et qui est appelée implicitement lors de la création de l'objet de la classe.
ou vous pouvez dire que pour créer un objet, vous devez appeler son constructeur. Si le constructeur n'est pas appelé, l'objet ne peut pas être instancié.
Cela signifie que si nous avons un constructeur privé dans une classe, ses objets ne peuvent être instanciés que dans la classe. Donc, en termes plus simples, vous pouvez dire que si le constructeur est privé, vous ne pourrez pas créer ses objets en dehors de la classe.
Quel est l'avantage Ce concept peut être mis en œuvre pour atteindre objet singleton (cela signifie qu'un seul objet de la classe peut être créé).
Voir le code suivant,
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.
}
}
Utilisation de Java Reflection comme suit:
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();
}
}
Oui, vous pouvez, comme mentionné par @Jon Steet.
Une autre façon d'accéder à un constructeur privé consiste à créer une méthode statique publique au sein de cette classe et à avoir son type de retour comme objet.
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();
}
}
Vous pouvez bien sûr accéder au constructeur privé à partir d'autres méthodes ou de constructeurs de la même classe et de ses classes internes. En utilisant la réflexion, vous pouvez également utiliser le constructeur privé ailleurs, à condition que SecurityManager ne vous empêche pas de le faire.
J'aime les réponses ci-dessus, mais il existe deux façons plus astucieuses de créer une nouvelle instance d'une classe ayant un constructeur privé. Tout dépend de ce que vous voulez atteindre et dans quelles circonstances.
Dans ce cas, vous devez démarrer la machine virtuelle Java avec un transformateur. Pour ce faire, vous devez implémenter un nouvel agent Java, puis faire en sorte que ce transformateur modifie le constructeur à votre place.
Commencez par créer le transformateur class . Cette classe a une méthode appelée transform. Remplacez cette méthode et dans cette méthode, vous pouvez utiliser ASM class reader et d'autres classes pour manipuler la visibilité de votre constructeur. Une fois le transformateur terminé, votre code client aura accès au constructeur.
Vous pouvez en savoir plus à ce sujet ici: Changer un constructeur Java privé avec ASM
Eh bien, ce n'est pas vraiment accéder au constructeur, mais vous pouvez quand même créer une instance. Supposons que vous utilisez une bibliothèque tierce (par exemple, Guava) et que vous avez accès au code, mais que vous ne souhaitez pas le modifier dans le fichier jar chargé par la machine virtuelle pour une raison quelconque (je sais, ceci est pas très réaliste, mais supposons que le code soit dans un conteneur partagé comme Jetty et que vous ne pouvez pas changer le code partagé, mais que vous avez un contexte de chargement de classe séparé), vous pouvez alors faire une copie du code tiers avec le constructeur privé, changez le constructeur privé à protected ou public dans votre code puis placez votre classe au début du classpath. À partir de ce moment, votre client peut utiliser le constructeur modifié et créer des instances.
Ce dernier changement s'appelle une couture link , qui est une sorte de couture où le point d'activation est le chemin de classe.
Oui, nous pouvons accéder au constructeur privé ou instancier une classe avec constructeur privé. L'API de réflexion Java et le modèle de conception singleton ont largement utilisé le concept d'accès au constructeur privé . De plus, les conteneurs d'infrastructure Spring peuvent accéder au constructeur privé de beans et ce framework a utilisé l'API de réflexion Java . Le code suivant illustre la façon d'accéder au constructeur privé.
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
J'espère que vous l'avez.
Regardez le motif Singleton. Il utilise un constructeur privé.
J'espère que cet exemple peut vous aider:
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();
}
}
}
Sortie: Constructeur privé appelé
Nous ne pouvons pas accéder à un constructeur privé en dehors de la classe, mais en utilisant l'API Java Reflection, nous pouvons accéder à un constructeur privé Veuillez trouver ci-dessous le 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));
}
}
}
}
Si vous utilisez Spring IoC, le conteneur Spring crée et injecte également un objet de la classe ayant un constructeur privé.
La réponse simple est oui, nous pouvons avoir des constructeurs privés dans Java .
Il existe différents scénarios d'utilisation de constructeurs privés. Les principaux sont
J'ai essayé comme ça ça marche. Donnez-moi une suggestion si je me trompe.
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();
}
}
}
Oui, vous pouvez instancier une instance avec un constructeur privé à l’aide de Reflection
, voir l’exemple collé ci-dessous tiré de Java2s pour comprendre comment
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();
}
}
}
Reflection est une API dans Java que nous pouvons utiliser pour appeler des méthodes au moment de l'exécution, quel que soit le spécificateur d'accès utilisé avec celles-ci. Pour accéder à un constructeur privé d'une classe:
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();
}
}
Lors de l'appel du constructeur, l'erreur Java.lang.UnsupportedOperationException: It is a utility call
Mais souvenez-vous d’utiliser l’application de réflexion pour causer des problèmes
Le principe de base pour avoir un constructeur privé est que le fait d'avoir un constructeur privé empêche l'accès d'un code autre que le code de sa propre classe à la création d'objets de cette classe.
Oui, nous pouvons avoir des constructeurs privés dans une classe et ils peuvent être rendus accessibles en créant des méthodes statiques qui créent à leur tour le nouvel objet pour la classe.
Class A{
private A(){
}
private static createObj(){
return new A();
}
Class B{
public static void main(String[]args){
A a=A.createObj();
}}
Donc, pour faire un objet de cette classe, l’autre classe doit utiliser les méthodes statiques.
Quel est l'intérêt d'avoir une méthode statique lorsque nous rendons le constructeur privé?
Les méthodes statiques existent de sorte que s’il est nécessaire de créer l’instance de cette classe, des vérifications prédéfinies peuvent être appliquées aux méthodes statiques avant la création de l’instance. Par exemple, dans une classe Singleton, la méthode statique vérifie si l'instance a déjà été créée ou non. Si l'instance est déjà créée, elle renvoie simplement cette instance plutôt que d'en créer une nouvelle.
public static MySingleTon getInstance(){
if(myObj == null){
myObj = new MySingleTon();
}
return myObj;
}