web-dev-qa-db-fra.com

Java: Différence entre Class.forName et ClassLoader.loadClass

Je suis récemment tombé sur un code qui m'a fait réfléchir. Quelle est la différence entre:

Class theClass = Class.forName("SomeImpl");
SomeImpl impl = (SomeImpl)theClass.newInstance();

et:

Class theClass = ClassLoader.loadClass("SomeImpl");
SomeImpl impl = (SomeImpl)theClass.newInstance();

Sont-ils synonymes? Est-ce que l'un est préférable à l'autre dans certaines circonstances? Quelles sont les choses à faire et à ne pas faire avec ces deux méthodes?

Merci d'avance.

39
IAmYourFaja

Class.forName () utilisera toujours le ClassLoader de l'appelant, alors que ClassLoader.loadClass () peut spécifier un ClassLoader différent. Je pense que Class.forName initialise également la classe chargée, alors que l'approche ClassLoader.loadClass () ne le fait pas tout de suite (elle n'est pas initialisée tant qu'elle n'est pas utilisée pour la première fois).

Je viens de trouver cet article lorsque je cherche à confirmer mon résumé du comportement d'initialisation. Il semble que cela contienne la plupart des informations que vous recherchez:

http://www.javaworld.com/javaworld/javaqa/2003-03/01-qa-0314-forname.html

Cet usage est plutôt cool, bien que je ne l'utilise jamais auparavant: 

Class.forName(String, boolean, ClassLoader)

Il vous permet de spécifier un ClassLoader et le paramètre boolean définit si la classe doit être initialisée lors du chargement ou non.

18
Shaun

La réponse de Shaun est plus ou moins correcte sauf quelques omissions/petites erreurs:

  • Class.forName associe la classe w/ClassLoader (même si un autre parent la charge pour de vrai), par conséquent, ClassLoader.findLoadedClass réussit la prochaine fois. C'est un point très, très important, la plupart des ClassLoader essaieraient Class c = findLoadedClass(name); if (c!=null) return c; en tant que premières instructions en contournant toute la partie recherche/recherche. L'appel direct de ClassLoader.load n'ajoutera pas la classe aux classes chargées.

Le cas a des implications lorsqu’il est chargé via la même structure graphique que ClassLoader, c’est-à-dire qu’on n’utilise pas uniquement le parent pour rechercher en premier.

  • L'initialisation de la classe est effectuée dans loadClass du ClassLoader avec le code suivant: if (resolve) resolveClass(c); et le ClassLoader peuvent en réalité éviter de résoudre le problème, non recommandé mais possible.

Quelles sont les choses à faire et à ne pas utiliser ces deux méthodes?

Sauf si vous avez une très bonne idée de la raison pour laquelle vous voulez ClassLoader.loadClass(String), ne l'utilisez pas directement. Dans tous les autres cas, utilisez toujours Class.forName(name, true, classLoader).

Le chargement global de la classe est à côté d'un art et il ne peut pas être couvert par une réponse simple (ne plaisante pas à propos de la partie artistique)

10
bestsss

Lorsque vous utilisez Class.forName("SomeImpl"), vous obtenez la classe via le chargeur de classes actuel (c'est-à-dire le chargeur de la classe dans laquelle vous appelez la méthode). Il sera également initialise la classe. C'est en fait la même chose que d'appeler Class.forName("SomeImpl", true, currentLoader)currentLoader serait le chargeur de classe de l'appelant. Voir les détails ici .

La deuxième méthode nécessite un chargeur de classe à choisir en premier. Ne l'écrivez pas comme ClassLoader.loadClass("SomeImpl") car c'est pas une méthode statique. Vous auriez besoin de quelque chose comme

final ClassLoader cl = this.getClass().getClassLoader();
Class theClass = cl.loadClass("SomeImpl");

Notez que les sous-classes de ClassLoader doivent remplacer la méthode findClass plutôt que loadClass. Cela revient à appeler la méthode (protégée) loadClass("SomeImpl", false), où le deuxième argument indique si la liaison doit être effectuée ou non.

Il existe des différences plus subtiles ... La méthode loadClass attend un nom de classe binaire tel que spécifié par la spécification du langage Java, tandis que forName peut également être utilisé avec des chaînes représentant des types primitifs ou des classes de tableau.

Globalement, il est préférable d'utiliser Class.forName, en spécifiant si nécessaire un chargeur de classe spécifique et en spécifiant s'il doit être initialisé ou non, puis laissez l'implémentation déterminer le reste. L'utilisation directe de chargeurs de classes est utile pour rechercher des ressources dans un fichier jar ou sur le chemin d'accès aux classes.

3
G_H

Cette ligne ne compilera pas:

Class theClass = ClassLoader.loadClass("SomeImpl");

car loadClass n'est pas une méthode statique de ClassLoader.

Pour résoudre ce problème, créez un objet ClassLoader de l'une des trois manières suivantes:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ClassLoader classLoader = Main.class.getClassLoader();      // Assuming in class Main
ClassLoader classLoader = getClass().getClassLoader();      // works in any class

puis appelez:

Class theClass = classLoader.loadClass("SomeImpl");

-dbednar

1
joe

La méthode loadClass() ne peut pas être appelée comme une variable static. Créez une sous-classe pour ClassLoader et utilisez d'autres méthodes supplémentaires pour effectuer des opérations. Peut créer votre propre chargeur de classe en étendant la classe ClassLoader. En pratique, les deux sens sont identiques.

0
Hari