¿Cuál es la diferencia entre "Class.forName ()" y "Class.forName (). NewInstance ()"?

165

¿Cuál es la diferencia entre Class.forName()y Class.forName().newInstance()?

No entiendo la diferencia significativa (¡he leído algo sobre ellos!). ¿Me podría ayudar?

Johanna
fuente

Respuestas:

247

Quizás un ejemplo que demuestre cómo se utilizan ambos métodos lo ayudará a comprender mejor las cosas. Entonces, considere la siguiente clase:

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

Como se explica en su javadoc, llamar devuelve el objeto asociado con la clase o interfaz con el nombre de cadena dado, es decir, devuelve el que está afectado a la variable de tipo .Class.forName(String) Classtest.Demo.classclazzClass

Luego, la llamada crea una nueva instancia de la clase representada por este objeto. La clase se instancia como si fuera una expresión con una lista de argumentos vacía. En otras palabras, esto es realmente equivalente a ay devuelve una nueva instancia de .clazz.newInstance() Classnewnew Demo()Demo

Y al ejecutar esta Democlase, se imprime el siguiente resultado:

Hi!

La gran diferencia con el tradicional newes que newInstancepermite crear una instancia de una clase que no conoce hasta el tiempo de ejecución, lo que hace que su código sea más dinámico.

Un ejemplo típico es la API JDBC que carga, en tiempo de ejecución, el controlador exacto requerido para realizar el trabajo. Los contenedores EJB, los contenedores Servlet son otros buenos ejemplos: utilizan la carga dinámica de tiempo de ejecución para cargar y crear componentes que no saben nada antes del tiempo de ejecución.

En realidad, si desea ir más allá, eche un vistazo al artículo de Ted Neward Understanding Class.forName () que estaba parafraseando en el párrafo anterior.

EDITAR (respondiendo una pregunta del OP publicado como comentario): El caso de los controladores JDBC es un poco especial. Como se explica en el capítulo DriverManager de Introducción a la API JDBC :

(...) Se Drivercarga una clase y, por lo tanto, se registra automáticamente con el DriverManager, de una de dos maneras:

  1. llamando al método Class.forName. Esto carga explícitamente la clase de controlador. Dado que no depende de ninguna configuración externa, esta forma de cargar un controlador es la recomendada para usar el DriverManager marco. El siguiente código carga la clase acme.db.Driver:

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

    Si acme.db.Driverse ha escrito de manera que al cargarlo, se crea una instancia y también se llama DriverManager.registerDrivercon esa instancia como parámetro (como debería hacerlo), entonces está en la DriverManagerlista de controladores y está disponible para crear una conexión.

  2. (...)

En ambos casos, es responsabilidad de la Driverclase recién cargada registrarse a sí misma llamando DriverManager.registerDriver. Como se mencionó, esto debe hacerse automáticamente cuando se carga la clase.

Para registrarse durante la inicialización, el controlador JDBC generalmente usa un bloque de inicialización estático como este:

package acme.db;

public class Driver {

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

    ...
}

La llamada Class.forName("acme.db.Driver")provoca la inicialización de la acme.db.Driverclase y, por lo tanto, la ejecución del bloque de inicialización estática. Y de Class.forName("acme.db.Driver")hecho "creará" una instancia, pero esto es solo una consecuencia de cómo se implementa (bueno) el controlador JDBC.

Como nota al margen, mencionaría que todo esto ya no es necesario con JDBC 4.0 (agregado como paquete predeterminado desde Java 7) y la nueva característica de carga automática de los controladores JDBC 4.0. Consulte las mejoras de JDBC 4.0 en Java SE 6 .

Pascal Thivent
fuente
2
en el sitio anterior está escrito que: "Llamar a Class.forName crea automáticamente una instancia de un controlador y lo registra con el DriverManager, por lo que no necesita crear una instancia de la clase. Si fuera a crear su propia instancia , estarías creando un duplicado innecesario, pero no haría daño ". significa que mediante Class.forName creará una instancia de forma automática y, si desea crear la otra, creará una instancia innecesaria. Por lo tanto, Calss.forName () y Class.forName (). newInstance () crearán una instancia de ¡¡conductor!!
Johanna
10
Los controladores JDBC son "especiales", se escriben con un bloque de inicialización estático donde se crea una instancia y se pasa como parámetro de DriverManager.registerDriver. Llamar Class.forNamea un controlador JDBC provoca su inicialización y, por lo tanto, la ejecución del bloque estático. Eche un vistazo a java2s.com/Open-Source/Java-Document/Database-DBMS/… para ver un ejemplo. Así que este es realmente un caso particular debido a las partes internas del controlador.
Pascal Thivent
1
Me di cuenta de que en otra respuesta , se desaconseja usar Class.newInstance (). Se recomienda usar Class.getConstructor (), seguido de Constructor.newInstance () a su vez. Evita enmascarar posibles excepciones.
LS
"newInstance permite crear una instancia de una clase que no conoces hasta el tiempo de ejecución" me alegró el día. Gracias.
Código entusiasta el
37

Class.forName () le proporciona el objeto de clase, que es útil para la reflexión. Los métodos que tiene este objeto los define Java, no el programador que escribe la clase. Son iguales para todas las clases. Llamar a newInstance () en eso le da una instancia de esa clase (es decir, llamarla Class.forName("ExampleClass").newInstance()es equivalente a llamar new ExampleClass()), en la que puede llamar a los métodos que define la clase, acceder a los campos visibles, etc.

Thomas Lötzer
fuente
29

En el mundo JDBC, la práctica normal (de acuerdo con la API JDBC) es que se usa Class#forName()para cargar un controlador JDBC. El controlador JDBC debería registrarse en DriverManagerun bloque estático:

package com.dbvendor.jdbc;

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

public class MyDriver implements Driver {

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

    public MyDriver() {
        //
    }

}

La invocación Class#forName()ejecutará todos los inicializadores estáticos . De esta manera, DriverManagerpuede encontrar el controlador asociado entre los controladores registrados por la URL de conexión durante la getConnection()cual se ve más o menos de la siguiente manera:

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

Pero también había controladores JDBC con errores , comenzando con el org.gjt.mm.mysql.Driverejemplo bien conocido, que se registra incorrectamente dentro del Constructor en lugar de un bloque estático:

package com.dbvendor.jdbc;

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

public class BadDriver implements Driver {

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

}

¡La única forma de hacerlo funcionar dinámicamente es llamar newInstance()después! De lo contrario, enfrentará a primera vista una "SQLException inexplicable: ningún controlador adecuado" Una vez más, este es un error en el controlador JDBC, no en su propio código. Hoy en día, ningún controlador JDBC debe contener este error. Así que puedes (y deberías) dejar de newInstance()lado.

BalusC
fuente
17

1: si solo le interesa el bloque estático de la clase, la carga de la clase solo funcionaría y ejecutaría bloques estáticos, entonces todo lo que necesita es:

Class.forName("Somthing");

2: si está interesado en cargar la clase, ejecute sus bloques estáticos y también desea acceder a su parte no estática, entonces necesita una instancia y luego necesita:

Class.forName("Somthing").newInstance();
Hussain Akhtar Wahid 'Ghouri'
fuente
Excelente respuesta! ¡Claro y conciso!
gaurav
6

Class.forName () obtiene una referencia a una clase, Class.forName (). NewInstance () intenta usar el constructor sin argumentos para que la clase devuelva una nueva instancia.

Gopi
fuente
3

"Class.forName ()" devuelve el tipo de clase para el nombre dado. "newInstance ()" devuelve una instancia de esta clase.

En el tipo no puede llamar directamente a ningún método de instancia, pero solo puede usar la reflexión para la clase. Si desea trabajar con un objeto de la clase, debe crear una instancia (igual que llamar a "new MyClass ()").

Ejemplo para "Class.forName ()"

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

Ejemplo para "Class.forName (). NewInstance ()"

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

simplemente agregando a las respuestas anteriores, cuando tenemos un código estático (es decir, el bloque de código es independiente de la instancia) que debe estar presente en la memoria, podemos tener la clase devuelta, por lo que usaremos Class.forname ("someName") de lo contrario si no tiene código estático, podemos usar Class.forname (). newInstance ("someName") ya que cargará bloques de código de nivel de objeto (no estáticos) en la memoria

sij
fuente
1

No importa cuántas veces llame al método Class.forName (), solo una vez que el bloque estático se ejecuta no varias veces:

paquete forNameMethodDemo;

clase pública 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();
    }

}

clase pública DemoClass {

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

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

}

la salida será:

in Static block in Instance block

Esta in Static blockdeclaración se imprime solo una vez, no tres veces.

Priyanka Wagh
fuente
0

Class.forName () -> forName () es el método estático de la clase Class, devuelve el objeto Class class utilizado para la reflexión, no el objeto class del usuario, por lo que solo puede llamar a los métodos Class class como getMethods (), getConstructors () etc.

Si solo le interesa ejecutar un bloque estático de su clase (Tiempo de ejecución dado) y solo obtener información de métodos, constructores, Modificador, etc. de su clase, puede hacerlo con este objeto que obtiene usando Class.forName ()

Pero si desea acceder o llamar a su método de clase (clase que ha dado en tiempo de ejecución), entonces necesita tener su objeto para que el método newInstance de Class class lo haga por usted. Crea una nueva instancia de la clase y se la devuelve Solo necesitas escribirlo a tu clase.

ex-: supongamos que Empleado es su clase entonces

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

// args [0] = argumento de línea cmd para dar clase en tiempo de ejecución.

Empleado ob1 = a.newInstance ();

a.newInstance () es similar a crear objetos usando new Employee ().

ahora puede acceder a todos los campos y métodos visibles de su clase.

Vinod Malkani
fuente