Quelle est la différence entre «Class.forName ()» et «Class.forName (). NewInstance ()»?

165

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!). Pourrais-tu m'aider s'il te plaît?

Johanna
la source

Réponses:

247

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

package test;

public class Demo {

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

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

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

Ensuite, l'appel crée une nouvelle instance de la classe représentée par cet objet. La classe est instanciée comme par une expression avec une liste d'arguments vide. En d'autres termes, c'est ici en fait équivalent à a et renvoie une nouvelle instance de .clazz.newInstance() Classnewnew Demo()Demo

Et exécuter cette Democlasse imprime ainsi la sortie suivante:

Hi!

La grande différence avec le traditionnel newest que cela newInstancepermet d'instancier une classe que vous ne connaissez pas avant l'exécution, ce qui rend votre code plus dynamique.

Un exemple typique est l'API JDBC qui charge, au moment de l'exécution, le pilote exact requis 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 au-dessus.

EDIT (répondant à une question de l'OP postée en commentaire): Le cas des pilotes JDBC est un peu particulier. Comme expliqué dans le chapitre DriverManager de Premiers pas avec l'API JDBC :

(...) Une Driverclasse est chargée, et donc automatiquement enregistrée avec le DriverManager, de l'une des deux manières suivantes:

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

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

    Si acme.db.Drivera été écrit de façon à ce que le chargement provoque la création d'une instance et appelle également DriverManager.registerDrivercette instance comme paramètre (comme il se doit), alors il est dans la DriverManagerliste des pilotes et disponible pour créer une connexion.

  2. (...)

Dans ces deux cas, il est de la responsabilité de la Driverclasse 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 lors de l'initialisation, le pilote JDBC utilise généralement un bloc d'initialisation statique comme celui-ci:

package acme.db;

public class Driver {

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

    ...
}

L'appel Class.forName("acme.db.Driver")provoque l'initialisation de la acme.db.Driverclasse et donc l'exécution du bloc d'initialisation statique. Et Class.forName("acme.db.Driver")va effectivement "créer" une instance, mais ce n'est qu'une conséquence de la façon dont les (bons) pilotes JDBC sont implémentés.

En remarque, je mentionnerais que tout cela n'est plus nécessaire avec JDBC 4.0 (ajouté comme 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 .

Pascal Thivent
la source
2
dans le site ci-dessus, il est écrit que: "L'appel de Class.forName crée automatiquement une instance d'un pilote et l'enregistre avec DriverManager, vous n'avez donc 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 ferait aucun mal. " cela signifie que par Class.forName vous créerez une instance de manière outomatiquement et si vous voulez créer l'autre, cela créera une instance inutile. Ainsi, Calss.forName () et Class.forName (). newInstance () créeront une instance du chauffeur!!
Johanna
10
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. L'appel Class.forNamesur 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. C'est donc un cas particulier en raison des composants internes du pilote.
Pascal Thivent
1
J'ai remarqué que dans une autre réponse , l'utilisation de Class.newInstance () est fortement déconseillée. Il est recommandé d'utiliser Class.getConstructor (), suivi de Constructor.newInstance () à son tour. Cela évite de masquer d'éventuelles exceptions.
LS
"newInstance permet d'instancier une classe que vous ne connaissez pas avant l'exécution" a fait ma journée. Merci.
Code Enthusiastic
37

Class.forName () vous donne l'objet de classe, ce qui est utile pour la réflexion. Les méthodes de cet objet sont définies par Java, pas par le programmeur qui écrit la classe. Ils sont les mêmes pour chaque classe. Appeler newInstance () sur cela vous donne une instance de cette classe (c'est-à-dire que l'appeler Class.forName("ExampleClass").newInstance()équivaut à appeler new ExampleClass()), sur laquelle vous pouvez appeler les méthodes que la classe définit, accéder aux champs visibles, etc.

Thomas Lötzer
la source
29

Dans le monde JDBC, la pratique normale (selon l'API JDBC) est que vous utilisez Class#forName()pour charger un pilote JDBC. Le pilote JDBC doit notamment s'enregistrer dans DriverManagerun 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'appel Class#forName()exécutera tous les initialiseurs statiques . De cette façon, vous DriverManagerpouvez trouver le pilote associé parmi les pilotes enregistrés par URL de connexion au cours de getConnection()laquelle ressemblent à peu près à 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("No suitable driver");
}

Mais il y avait aussi bogué pilotes JDBC, en commençant par l' org.gjt.mm.mysql.Driverexemple aussi bien connu, qui s'enregistre de manière incorrecte à l' intérieur du 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()après! Sinon, vous serez confronté à première vue "SQLException inexplicable: pas de pilote approprié". Encore une fois, il s'agit d'un bogue dans le pilote JDBC, pas dans votre propre code. De nos jours, aucun pilote JDBC ne devrait contenir ce bogue. Ainsi, vous pouvez (et devriez) laisser de newInstance()côté.

BalusC
la source
17

1: si vous êtes intéressé uniquement par le bloc statique de la classe, le chargement de la classe ne ferait que faire, et exécuterait des blocs statiques alors tout ce dont vous avez besoin est:

Class.forName("Somthing");

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

Class.forName("Somthing").newInstance();
Hussain Akhtar Wahid 'Ghouri'
la source
Excellente réponse! Clair et concis!
gaurav
6

Class.forName () obtient une référence à une classe, Class.forName (). NewInstance () essaie d'utiliser le constructeur sans argument pour que la classe retourne une nouvelle instance.

Gopi
la source
3

"Class.forName ()" renvoie le type de classe pour le nom donné. "newInstance ()" renvoie une instance de cette classe.

Sur le type, vous ne pouvez appeler directement aucune méthode d'instance, mais vous pouvez uniquement utiliser la réflexion pour la classe. Si vous voulez travailler avec un objet de la classe, vous devez en créer une instance (comme pour appeler "new MyClass ()").

Exemple pour "Class.forName ()"

Class myClass = Class.forName("test.MyClass");
System.out.println("Number of public methods: " + myClass.getMethods().length);

Exemple pour "Class.forName (). NewInstance ()"

MyClass myClass = (MyClass) Class.forName("test.MyClass").newInstance();
System.out.println("String representation of MyClass instance: " + myClass.toString());
Arne Deutsch
la source
3

en ajoutant simplement aux réponses ci-dessus, lorsque nous avons un code statique (c'est-à-dire que le bloc de code est indépendant de l'instance) qui doit être présent en mémoire, nous pouvons avoir la classe retournée donc nous utiliserons Class.forname ("someName") sinon si nous ne pas avoir de code statique, nous pouvons utiliser Class.forname (). newInstance ("someName") car il chargera des blocs de code de niveau objet (non statiques) en mémoire

sij
la source
1

Peu importe le nombre de fois que vous appelez la méthode Class.forName (), Une fois que le bloc statique est exécuté, pas plusieurs fois:

package forNameMethodDemo;

classe publique MainClass {

    public static void main(String[] args) throws Exception {
        Class.forName("forNameMethodDemo.DemoClass");
        Class.forName("forNameMethodDemo.DemoClass");
        Class.forName("forNameMethodDemo.DemoClass");
        DemoClass demoClass = (DemoClass)Class.forName("forNameMethodDemo.DemoClass").newInstance();
    }

}

public class DemoClass {

static {
    System.out.println("in Static block");
}

{
    System.out.println("in Instance block");
}

}

la sortie sera:

in Static block in Instance block

Cette in Static blockdéclaration est imprimée une seule fois pas trois fois.

Priyanka Wagh
la source
0

Class.forName () -> forName () est la méthode statique de la classe Class, elle retourne un objet de classe Class utilisé pour la réflexion et non un objet de classe utilisateur, vous ne pouvez donc appeler que des méthodes de classe Class comme getMethods (), getConstructors () etc.

Si vous vous souciez d'exécuter uniquement le bloc statique de votre classe (Runtime donné) et d'obtenir uniquement des informations sur les méthodes, les constructeurs, le modificateur, etc. de votre classe, vous pouvez le faire avec cet objet que vous obtenez en utilisant Class.forName ()

Mais si vous voulez accéder ou appeler votre méthode de classe (classe que vous avez donnée au moment de l'exécution), vous devez avoir son objet afin que la méthode newInstance de la classe Class le fasse pour vous.Il crée une nouvelle instance de la classe et vous la renvoie Il vous suffit de le transtyper dans votre classe.

ex-: supposons que l'employé soit votre classe alors

Classe a = Class.forName (args [0]);

// args [0] = argument de ligne cmd pour donner la classe à l'exécution.

Employé ob1 = a.newInstance ();

a.newInstance () est similaire à la création d'un objet à l'aide de new Employee ().

vous pouvez maintenant accéder à tous les champs et méthodes visibles de votre classe.

Vinod Malkani
la source