42 votes

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

Quelle est la différence entre Class.forName y ClassLoader.loadClass dans les codes suivants :

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

y

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

Sont-ils synonymes ? L'une est-elle préférable à l'autre dans certaines circonstances ? Quelles sont les choses à faire et à ne pas faire lorsqu'on utilise ces deux méthodes ?

0 votes

Personnellement, j'essaie de ne pas utiliser de chaînes littérales dans mon code... Pensez à écrire SomeImpl.class.getSimpleName() au lieu de la valeur littérale codée en dur (ce qui évite les maux de tête lors du renommage de la classe).

8 votes

@Yuval : D'ordinaire, je serais d'accord avec vous, mais dans le cas de Class.forName() le site point final c'est que tu lui passes une chaîne de caractères. Si vous avez un Class<SomeImpl> qui traîne, alors vous n'avez pas besoin d'appeler Class.forName() . Et si vous pouvez écrire SomeImpl.class alors vous pouvez écrire new SomeImpl() au lieu d'utiliser la réflexion tout court.

2 votes

@Yuval, le chargement dynamique des classes est une fonctionnalité essentielle de Java et n'a rien à voir avec l'utilisation d'une chaîne au lieu d'une classe. [D. Pryden en parle déjà].

20voto

Shaun Points 1842

Class.forName() utilisera toujours le ClassLoader de l'appelant, alors que ClassLoader.loadClass() peut spécifier un ClassLoader différent. Je crois 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 avant sa première utilisation).

Je viens de trouver cet article en cherchant à confirmer mon résumé du comportement d'initialisation. Il semble qu'il 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'aie jamais utilisé auparavant :

Class.forName(String, boolean, ClassLoader)

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

2 votes

Il utilisera uniquement le classloader de l'appelant si vous n'en fournissez pas. Extrait du même article : "...Si le choix d'un chargeur spécifique pour charger la classe est important pour votre conception, vous devriez utiliser ClassLoader.loadClass() ou la version à trois paramètres de forName() ajouté dans la plate-forme Java 2, édition standard (J2SE) : Class.forName(String, boolean, ClassLoader) ."

11voto

bestsss Points 6403

La réponse de Shaun est plus ou moins correcte à l'exception de quelques omissions/petites erreurs :

  • Class.forName associe la classe avec le ClassLoader (indépendamment du fait qu'un autre parent la charge réellement), d'où ClassLoader.findLoadedClass est réussie la prochaine fois. C'est un point très, très important, la plupart des ClassLoader essaieraient de Class c = findLoadedClass(name); if (c!=null) return c; comme premières déclarations en contournant toute la partie recherche/registre. L'appel direct à ClassLoader.load n'ajoutera pas la classe à celles qui sont chargées.

Ce cas a des implications lorsqu'il est chargé via la structure similaire au graphe de ClassLoader, c'est-à-dire qu'il n'utilise pas le parent uniquement pour rechercher en premier.

  • L'initialisation de la classe est effectuée dans loadClass du ClassLoader avec un code comme celui-ci : if (resolve) resolveClass(c); et le ClassLoader peut en fait ne pas le résoudre, ce qui n'est pas recommandé mais possible.

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

A moins que vous n'ayez une idée très précise de la raison pour laquelle vous voulez ClassLoader.loadClass(String) ne l'utilisez pas directement. Dans tous les autres cas, faites toujours confiance à Class.forName(name, true, classLoader) .

Dans l'ensemble, le chargement des classes est presque un art et il ne peut pas être couvert par une réponse simple (je ne plaisante pas avec la partie art).

3voto

G_H Points 5979

Quand vous utilisez Class.forName("SomeImpl") vous obtenez la classe via le chargeur de classe actuel (c'est-à-dire le chargeur de la classe dans laquelle vous effectuez l'appel de méthode). Il sera également initialiser la classe. C'est effectivement 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 aquí .

La deuxième méthode nécessite de choisir d'abord un classloader. Ne l'écrivez pas comme ClassLoader.loadClass("SomeImpl") puisqu'il s'agit pas une méthode statique. Vous auriez besoin de quelque chose comme

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

N'oubliez pas que les sous-classes de ClassLoader doivent surcharger la fonction findClass plutôt que loadClass . C'est la même chose que d'appeler la méthode (protégée) loadClass("SomeImpl", false) où le second argument indique si la liaison doit être effectuée ou non.

Il existe des différences plus subtiles... Le site loadClass s'attend à un nom de classe binaire tel que spécifié par la spécification du langage Java, alors que la méthode forName pourrait également être utilisé avec des chaînes de caractères représentant des types primitifs ou des classes de tableaux.

Dans l'ensemble, il est préférable d'utiliser Class.forName si nécessaire, en spécifiant un chargeur de classe spécifique et en indiquant s'il doit être initialisé ou non, puis en laissant l'implémentation s'occuper du reste. L'utilisation directe des chargeurs de classes permet de trouver des ressources dans un jar ou sur le classpath.

1voto

joe Points 49

Cette ligne ne compile 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 comme suit, de l'une des 3 manières possibles :

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

alors appelez :

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

-dbednar

0voto

Hari Points 1

En loadClass() ne peut pas être appelée comme une méthode static un. Créez une sous-classe pour ClassLoader et disposent de quelques autres méthodes supplémentaires pour effectuer des opérations. Vous pouvez créer votre propre chargeur de classe en étendant ClassLoader classe. Dans le domaine fonctionnel, les deux voies sont identiques.

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