Qual è la differenza tra “Class.forName ()” e “Class.forName (). NewInstance ()”?


165

Qual è la differenza tra Class.forName()e Class.forName().newInstance()?

Non capisco la differenza significativa (ho letto qualcosa su di loro!). Per favore potete aiutarmi?

Risposte:


247

Forse un esempio che dimostra come vengono utilizzati entrambi i metodi ti aiuterà a capire meglio le cose. Quindi, considera la seguente classe:

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();
    }
}

Come spiegato nel suo javadoc, la chiamata restituisce l' oggetto associato alla classe o all'interfaccia con il nome di stringa dato, cioè restituisce quale è influenzato dalla variabile di tipo .Class.forName(String) Classtest.Demo.classclazzClass

Quindi, chiamando crea una nuova istanza della classe rappresentata da questo oggetto. La classe viene istanziata come da un'espressione con un elenco di argomenti vuoto. In altre parole, questo è effettivamente equivalente a e restituisce una nuova istanza di .clazz.newInstance() Classnewnew Demo()Demo

E l'esecuzione di questa Democlasse stampa quindi il seguente output:

Hi!

La grande differenza con il tradizionale newè che newInstanceconsente di creare un'istanza di una classe che non si conosce fino al runtime, rendendo il codice più dinamico.

Un tipico esempio è l'API JDBC che carica, in fase di esecuzione, il driver esatto richiesto per eseguire il lavoro. I contenitori EJB, i contenitori servlet sono altri buoni esempi: usano il caricamento dinamico del runtime per caricare e creare componenti che non sanno nulla prima del runtime.

In realtà, se vuoi andare oltre, dai un'occhiata al documento di Ted Neward Understanding Class.forName () che stavo parafrasando nel paragrafo sopra.

EDIT (rispondendo a una domanda dell'OP pubblicata come commento): il caso dei driver JDBC è un po 'speciale. Come spiegato nel capitolo DriverManager di Introduzione all'API JDBC :

(...) Una Driverclasse viene caricata, e quindi automaticamente registrata con DriverManager, in uno dei due modi seguenti:

  1. chiamando il metodo Class.forName. Questo carica esplicitamente la classe del driver. Poiché non dipende da alcuna configurazione esterna, questo modo di caricare un driver è quello consigliato per l'utilizzo del DriverManager framework. Il codice seguente carica la classe acme.db.Driver:

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

    Se acme.db.Driverè stato scritto in modo tale che il caricamento causi la creazione di un'istanza e la chiama anche DriverManager.registerDrivercon quell'istanza come parametro (come dovrebbe fare), allora è DriverManagernell'elenco dei driver e disponibile per la creazione di una connessione.

  2. (...)

In entrambi questi casi, è responsabilità della Driverclasse appena caricata registrarsi chiamando DriverManager.registerDriver. Come accennato, questo dovrebbe essere fatto automaticamente quando la classe viene caricata.

Per registrarsi durante l'inizializzazione, il driver JDBC utilizza in genere un blocco di inizializzazione statico come questo:

package acme.db;

public class Driver {

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

    ...
}

La chiamata Class.forName("acme.db.Driver")provoca l'inizializzazione della acme.db.Driverclasse e quindi l'esecuzione del blocco di inizializzazione statica. E Class.forName("acme.db.Driver")in effetti "creerà" un'istanza, ma questa è solo una conseguenza di come (buono) driver JDBC sono implementati.

Come nota a margine, vorrei menzionare che tutto ciò non è più necessario con JDBC 4.0 (aggiunto come pacchetto predefinito da Java 7) e la nuova funzionalità di caricamento automatico dei driver JDBC 4.0. Vedi i miglioramenti di JDBC 4.0 in Java SE 6 .



2
nel sito sopra è scritto che: "La chiamata a Class.forName crea automaticamente un'istanza di un driver e la registra con DriverManager, quindi non è necessario creare un'istanza della classe. Se si dovesse creare la propria istanza , creeresti un duplicato non necessario, ma non danneggerebbe ". significa che con Class.forName creerai un'istanza in modo automatico e se vuoi crearne un'altra, creerà un'istanza non necessaria. Quindi sia Calss.forName () che Class.forName (). newInstance () creeranno un'istanza del autista !!
Johanna,

10
I driver JDBC sono "speciali", sono scritti con un blocco di inizializzazione statico in cui viene creata un'istanza e passata come parametro di DriverManager.registerDriver. La chiamata Class.forNamea un driver JDBC provoca la sua inizializzazione e quindi l'esecuzione del blocco statico. Dai un'occhiata a java2s.com/Open-Source/Java-Document/Database-DBMS/… per un esempio. Quindi questo è in realtà un caso particolare a causa dei driver interni.
Pascal Thivent,

1
Ho notato che in un'altra risposta , l'utilizzo di Class.newInstance () è fortemente scoraggiato. Si consiglia di utilizzare Class.getConstructor (), seguito a sua volta da Constructor.newInstance (). Evita di mascherare possibili eccezioni.
LS

"newInstance permette di creare un'istanza di una classe che non conosci fino al runtime" ha reso la mia giornata. Grazie.
Codice entusiasta

37

Class.forName () ti dà l'oggetto di classe, che è utile per la riflessione. I metodi che ha questo oggetto sono definiti da Java, non dal programmatore che scrive la classe. Sono gli stessi per ogni classe. Chiamare newInstance () su che ti dà un'istanza di quella classe (cioè chiamarla Class.forName("ExampleClass").newInstance()equivale a chiamare new ExampleClass()), su cui puoi chiamare i metodi che la classe definisce, accedere ai campi visibili ecc.


29

Nel mondo JDBC, la pratica normale (secondo l'API JDBC) è che si utilizza Class#forName()per caricare un driver JDBC. Il driver JDBC dovrebbe in particolare registrarsi DriverManagerall'interno di un blocco statico:

package com.dbvendor.jdbc;

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

public class MyDriver implements Driver {

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

    public MyDriver() {
        //
    }

}

Il richiamo Class#forName()eseguirà tutti gli inizializzatori statici . In questo modo è DriverManagerpossibile trovare il driver associato tra i driver registrati tramite l'URL di connessione durante il getConnection()quale appare approssimativamente il seguente:

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");
}

Ma c'erano anche driver JDBC difettosi , a partire dal org.gjt.mm.mysql.Drivertanto noto esempio, che si registra erroneamente all'interno del costruttore anziché in un blocco statico:

package com.dbvendor.jdbc;

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

public class BadDriver implements Driver {

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

}

L'unico modo per farlo funzionare in modo dinamico è chiamare newInstance()dopo! Altrimenti dovrai affrontare a prima vista inspiegabile "SQLException: nessun driver adatto". Ancora una volta, questo è un bug nel driver JDBC, non nel tuo codice. Al giorno d'oggi, nessun driver JDBC dovrebbe contenere questo errore. Quindi puoi (e dovresti) lasciare la newInstance()strada.


17

1: se sei interessato solo al blocco statico della classe, il caricamento della sola classe lo farebbe ed eseguirà blocchi statici quindi tutto ciò che ti serve è:

Class.forName("Somthing");

2: se sei interessato a caricare la classe, esegui i suoi blocchi statici e vuoi anche accedere alla sua parte non statica, allora hai bisogno di un'istanza e quindi hai bisogno di:

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

Risposta eccellente! Chiaro e conciso!
gaurav,

6

Class.forName () ottiene un riferimento a Class, Class.forName (). NewInstance () tenta di utilizzare il costruttore no-arg per la classe per restituire una nuova istanza.


3

"Class.forName ()" restituisce il tipo di classe per il nome specificato. "newInstance ()" restituisce un'istanza di questa classe.

Sul tipo non è possibile chiamare direttamente alcun metodo di istanza ma è possibile utilizzare solo la riflessione per la classe. Se vuoi lavorare con un oggetto della classe devi crearne un'istanza (come chiamare "new MyClass ()").

Esempio per "Class.forName ()"

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

Esempio per "Class.forName (). NewInstance ()"

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

3

aggiungendo solo le risposte sopra, quando abbiamo un codice statico (cioè il blocco di codice è indipendente dall'istanza) che deve essere presente in memoria, possiamo avere la classe restituita, quindi useremo Class.forname ("someName") altrimenti se noi non abbiamo un codice statico, possiamo usare Class.forname (). newInstance ("someName") in quanto caricherà blocchi di codice a livello di oggetto (non statici) in memoria


1

Indipendentemente da quante volte si chiama il metodo Class.forName (), solo una volta che il blocco statico viene eseguito non più volte:

pacchetto perNomeMetodoDemo;

classe pubblica 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();
    }

}

DemoClass di classe pubblica {

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

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

}

l'output sarà:

in Static block in Instance block

Questa in Static blockaffermazione viene stampata solo una volta e non tre volte.


0

Class.forName () -> forName () è il metodo statico della classe Class che restituisce l'oggetto classe Class utilizzato per la riflessione non oggetto classe utente quindi è possibile chiamare solo metodi di classe Class su di esso come getMethods (), getConstructors () ecc.

Se ti interessa solo eseguire il blocco statico della tua classe (Runtime dato) e ottenere solo informazioni su metodi, costruttori, modificatore ecc. Della tua classe, puoi fare con questo oggetto che ottieni usando Class.forName ()

Ma se vuoi accedere o chiamare il tuo metodo di classe (classe che hai dato in fase di runtime) allora devi avere il suo oggetto così newInstance metodo della classe Class farlo per te. Crea una nuova istanza della classe e te lo restituisce Devi solo lanciarlo nella tua classe.

es: supponiamo che il dipendente sia la tua classe allora

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

// args [0] = argomento della riga cmd per fornire la classe in fase di esecuzione.

Employee ob1 = a.newInstance ();

a.newInstance () è simile alla creazione di un oggetto utilizzando new Employee ().

ora puoi accedere a tutti i campi e metodi visibili della tua classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.