179 votes

Quelle est la différence entre "Class.forName()" et "Class.forName().newInstance()" ?

Quelle est la différence entre Class.forName() et Class.forName().newInstance() ?

Je ne comprends pas la différence significative (j'ai lu quelque chose à leur sujet !). Pourriez-vous m'aider s'il vous plaît ?

266voto

Pascal Thivent Points 295221

Peut-être qu'un exemple démontrant comment les deux méthodes sont utilisées vous aidera à mieux comprendre les choses. Ainsi, considérez la classe suivante:

package test;

public class Demo {

    public Demo() {
        System.out.println("Salut!");
    }

    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("test.Demo");
        Demo demo = (Demo) clazz.newInstance();
    }
}

Comme expliqué dans sa javadoc, l'appel de Class.forName(String) retourne l'objet Class associé à la classe ou à l'interface avec le nom de chaîne donné c'est-à-dire qu'il retourne test.Demo.class qui est affecté à la variable clazz de type Class.

Ensuite, l'appel de clazz.newInstance() crée une nouvelle instance de la classe représentée par cet objet Class. La classe est instanciée comme par une expression new avec une liste d'arguments vide. En d'autres termes, c'est en réalité équivalent à un new Demo() et retourne une nouvelle instance de Demo.

Et l'exécution de cette classe Demo affiche donc la sortie suivante:

Salut!

La grande différence avec le traditionnel new est que newInstance permet d'instancier une classe dont vous ne connaissez pas le nom avant l'exécution, rendant votre code plus dynamique.

Un exemple typique est l'API JDBC qui charge, à l'exécution, le pilote exact nécessaire pour effectuer le travail. Les conteneurs EJB, les conteneurs Servlet sont d'autres bons exemples: ils utilisent le chargement dynamique à l'exécution pour charger et créer des composants dont ils ne savent rien avant l'exécution.

En fait, si vous voulez aller plus loin, jetez un œil à l'article de Ted Neward Understanding Class.forName() que je paraphrasais dans le paragraphe juste ci-dessus.

ÉDITION (répondant à une question de l'auteur du message publiée en commentaire): Le cas des pilotes JDBC est un peu spécial. Comme expliqué dans le chapitre du DriverManager de Getting Started with the JDBC API:

(...) Une classe Driver est chargée, et par conséquent enregistrée automatiquement avec le DriverManager, de l'une des deux manières suivantes:

  1. en appelant la méthode Class.forName. Cela charge explicitement la classe du pilote. Comme cela ne dépend d'aucune configuration externe, cette façon de charger un pilote est celle recommandée pour utiliser le cadre du DriverManager. Le code suivant charge la classe acme.db.Driver:

     Class.forName("acme.db.Driver");

Si acme.db.Driver a été écrit de manière à ce que son chargement entraîne la création d'une instance et appelle également DriverManager.registerDriver avec cette instance en tant que paramètre (comme il devrait le faire), alors il se trouve dans la liste des pilotes de DriverManager et disponible pour la création d'une connexion.

  1. (...)

Dans ces deux cas, il incombe à la classe Driver nouvellement chargée de s'enregistrer en appelant DriverManager.registerDriver. Comme mentionné, cela devrait être fait automatiquement lorsque la classe est chargée.

Pour s'enregistrer pendant l'initialisation, les pilotes JDBC utilisent généralement un bloc d'initialisation statique comme ceci:

package acme.db;

public class Driver {

    static {
        java.sql.DriverManager.registerDriver(new Driver());
    }

    ...
}

Appeler Class.forName("acme.db.Driver") provoque l'initialisation de la classe acme.db.Driver et donc l'exécution du bloc d'initialisation statique. Et Class.forName("acme.db.Driver") va en effet "créer" une instance mais c'est simplement une conséquence de la manière dont les bons pilotes JDBC sont implémentés.

En passant, je mentionnerais que tout cela n'est plus nécessaire avec JDBC 4.0 (ajouté en tant que package par défaut depuis Java 7) et la nouvelle fonctionnalité de chargement automatique des pilotes JDBC 4.0. Voir Les améliorations de JDBC 4.0 dans Java SE 6.

0 votes

2 votes

Sur le site ci-dessus, il est écrit que : "Appeler la Class.forName crée automatiquement une instance d'un pilote et l'enregistre avec le Gestionnaire de pilotes, donc vous n'avez pas besoin de créer une instance de la classe. Si vous deviez créer votre propre instance, vous créeriez un doublon inutile, mais cela ne causerait aucun dommage." cela signifie que en utilisant Class.forName vous créerez automatiquement une instance et si vous voulez en créer une autre, cela créerait une instance inutile. Donc à la fois Calss.forName() et Class.forName().newInstance() créeront une instance du pilote !!

11 votes

Les pilotes JDBC sont "spéciaux", ils sont écrits avec un bloc d'initialisation statique où une instance est créée et passée en paramètre de DriverManager.registerDriver. Appeler Class.forName sur un pilote JDBC provoque son initialisation et donc l'exécution du bloc statique. Jetez un œil à java2s.com/Open-Source/Java-Document/Database-DBMS/… pour un exemple. Donc c'est en fait un cas particulier en raison des fonctionnalités internes du pilote.

37voto

Thomas Lötzer Points 8388

Class.forName() vous donne l'objet de classe, qui est utile pour la réflexion. Les méthodes que cet objet possède sont définies par Java, et non par le programmeur écrivant la classe. Elles sont les mêmes pour toutes les classes. Appeler newInstance() sur cet objet vous donne une instance de cette classe (c'est-à-dire en appelant Class.forName("ExampleClass").newInstance() c'est équivalent à appeler new ExampleClass()), sur laquelle vous pouvez appeler les méthodes que la classe définit, accéder aux champs visibles etc.

33voto

BalusC Points 498232

Dans le monde JDBC, la pratique normale (selon l'API JDBC) est d'utiliser Class#forName() pour charger un pilote JDBC. Le pilote JDBC devrait enregistrer lui-même dans DriverManager à l'intérieur d'un bloc statique :

package com.dbvendor.jdbc;

import java.sql.Driver;
import java.sql.DriverManager;

public class MyDriver implements Driver {

    static {
        DriverManager.registerDriver(new MyDriver());
    }

    public MyDriver() {
        //
    }

}

L'invocation de Class#forName() exécutera tous les initialiseurs statiques. De cette manière, le DriverManager peut trouver le pilote associé parmi les pilotes enregistrés par URL de connexion lors de getConnection() qui ressemble grossièrement à ce qui suit :

public static Connection getConnection(String url) throws SQLException {
    for (Driver driver : registeredDrivers) {
        if (driver.acceptsURL(url)) {
            return driver.connect(url);
        }
    }
    throw new SQLException("Aucun pilote approprié");
}

Mais il y avait aussi des pilotes JDBC erronés, à commencer par le org.gjt.mm.mysql.Driver comme exemple bien connu, qui s'enregistrent incorrectement dans le Constructeur au lieu d'un bloc statique :

package com.dbvendor.jdbc;

import java.sql.Driver;
import java.sql.DriverManager;

public class BadDriver implements Driver {

    public BadDriver() {
        DriverManager.registerDriver(this);
    }

}

La seule façon de le faire fonctionner dynamiquement est d'appeler newInstance() ensuite ! Sinon, vous serez confronté à première vue à une "SQLException: no suitable driver" inexplicable. Encore une fois, c'est un bug dans le pilote JDBC, pas dans votre propre code. De nos jours, aucun pilote JDBC ne devrait contenir ce bug. Vous pouvez (et devriez) laisser de côté le newInstance().

20voto

1 : si vous êtes intéressé uniquement par le bloc statique de la classe, charger uniquement la classe le ferait, et exécuterait les blocs statiques dont vous avez besoin :

Class.forName("Something");

2 : si vous êtes intéressé par le chargement de la classe, l'exécution de ses blocs statiques et que vous voulez également accéder à sa partie non statique, alors vous avez besoin d'une instance et ensuite vous avez besoin de :

Class.forName("Something").newInstance();

0 votes

Excellente réponse! Claire et concise!

7voto

Tech Jerk Points 1728

La méthode Class.forName() obtient une référence à une classe, Class.forName().newInstance() tente d'utiliser le constructeur sans argument de la classe pour renvoyer une nouvelle instance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X