Qual é a diferença entre "Class.forName ()" e "Class.forName (). NewInstance ()"?

165

Qual é a diferença entre Class.forName()e Class.forName().newInstance()?

Eu não entendo a diferença significativa (eu li algo sobre eles!). Podes ajudar-me, por favor?

Johanna
fonte

Respostas:

247

Talvez um exemplo que demonstre como os dois métodos são usados ​​o ajudará a entender melhor as coisas. Portanto, considere a seguinte 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();
    }
}

Conforme explicado em seu javadoc, a chamada retorna o objeto associado à classe ou interface com o nome da string, ou seja, retorna o que é afetado pela variável do tipo .Class.forName(String) Classtest.Demo.classclazzClass

Em seguida, a chamada cria uma nova instância da classe representada por este objeto. A classe é instanciada como se por uma expressão com uma lista de argumentos vazia. Em outras palavras, isso aqui é realmente equivalente a e retorna uma nova instância de .clazz.newInstance() Classnewnew Demo()Demo

E a execução dessa Democlasse imprime a seguinte saída:

Hi!

A grande diferença com o tradicional newé que newInstancepermite instanciar uma classe que você não conhece até o tempo de execução, tornando seu código mais dinâmico.

Um exemplo típico é a API JDBC que carrega, em tempo de execução, o driver exato necessário para executar o trabalho. Contêineres EJBs, contêineres Servlet são outros bons exemplos: eles usam o carregamento dinâmico do tempo de execução para carregar e criar componentes que não sabiam nada antes do tempo de execução.

Na verdade, se você quiser ir além, dê uma olhada no artigo de Ted Neward, Entendendo Class.forName (), que eu estava parafraseando no parágrafo acima.

EDIT (respondendo a uma pergunta do OP postado como comentário): O caso dos drivers JDBC é um pouco especial. Conforme explicado no capítulo DriverManager de Introdução à API JDBC :

(...) Uma Driverclasse é carregada e, portanto, automaticamente registrada no DriverManager, de uma das duas maneiras:

  1. chamando o método Class.forName. Isso carrega explicitamente a classe do driver. Como não depende de nenhuma configuração externa, essa maneira de carregar um driver é a recomendada para o uso da DriverManager estrutura. O código a seguir carrega a classe acme.db.Driver:

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

    Se acme.db.Driverfoi gravado para que o carregamento faça com que uma instância seja criada e também chame DriverManager.registerDriveressa instância como parâmetro (como deve ser o caso), ele estará na DriverManagerlista de drivers e estará disponível para criar uma conexão.

  2. (...)

Nos dois casos, é de responsabilidade da Driverclasse recém-carregada se registrar chamando DriverManager.registerDriver. Como mencionado, isso deve ser feito automaticamente quando a classe é carregada.

Para se registrar durante a inicialização, o driver JDBC normalmente usa um bloco de inicialização estático como este:

package acme.db;

public class Driver {

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

    ...
}

A chamada Class.forName("acme.db.Driver")causa a inicialização da acme.db.Driverclasse e, portanto, a execução do bloco de inicialização estática. E de Class.forName("acme.db.Driver")fato "criará" uma instância, mas isso é apenas uma consequência de como (bom) Driver JDBC é implementado.

Como uma observação lateral, eu mencionaria que tudo isso não é mais necessário com o JDBC 4.0 (adicionado como um pacote padrão desde o Java 7) e o novo recurso de carregamento automático dos drivers JDBC 4.0. Consulte Aprimoramentos do JDBC 4.0 no Java SE 6 .

Pascal Thivent
fonte
2
no site acima, está escrito que: "A chamada de Class.forName cria automaticamente uma instância de um driver e a registra no DriverManager, para que você não precise criar uma instância da classe. Se você criar sua própria instância , você criaria uma duplicata desnecessária, mas não faria mal ". isso significa que, por Class.forName, você criará uma instância automaticamente, e se desejar criar a outra, criará uma instância desnecessária. Portanto, tanto Calss.forName () quanto Class.forName (). newInstance () criará uma instância do motorista!!
Johanna
10
Os drivers JDBC são "especiais", são gravados com um bloco de inicialização estático no qual uma instância é criada e transmitida como parâmetro de DriverManager.registerDriver. A chamada Class.forNamede um driver JDBC causa sua inicialização e, portanto, a execução do bloco estático. Dê uma olhada em java2s.com/Open-Source/Java-Document/Database-DBMS/… para obter um exemplo. Portanto, esse é realmente um caso específico, devido aos componentes internos do driver.
precisa saber é o seguinte
1
Percebi que em outra resposta , o uso de Class.newInstance () é fortemente desencorajado. É recomendável usar Class.getConstructor (), seguido por Constructor.newInstance () por sua vez. Evita mascarar possíveis exceções.
LS
"newInstance permite instanciar uma classe que você não conhece até o tempo de execução" fez o meu dia. Obrigado.
Code Enthusiastic
37

Class.forName () fornece o objeto de classe, que é útil para reflexão. Os métodos que esse objeto possui são definidos por Java, não pelo programador que está escrevendo a classe. Eles são os mesmos para todas as classes. A chamada newInstance () fornece uma instância dessa classe (ou seja, chamá- Class.forName("ExampleClass").newInstance()lo é equivalente a chamar new ExampleClass()), na qual você pode chamar os métodos que a classe define, acessar os campos visíveis etc.

Thomas Lötzer
fonte
29

No mundo JDBC, a prática normal (de acordo com a API JDBC) é que você use Class#forName()para carregar um driver JDBC. O driver JDBC deve se registrar DriverManagerdentro de um bloco 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() {
        //
    }

}

A chamada Class#forName()executará todos os inicializadores estáticos . Dessa forma, é DriverManagerpossível encontrar o driver associado entre os drivers registrados pelo URL de conexão, durante o getConnection()qual aproximadamente se parece com:

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

Mas também havia drivers JDBC com erros , começando com o org.gjt.mm.mysql.Driverexemplo bem conhecido, que se registra incorretamente dentro do Construtor, em vez de um bloco estático:

package com.dbvendor.jdbc;

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

public class BadDriver implements Driver {

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

}

A única maneira de fazê-lo funcionar dinamicamente é ligar newInstance()depois! Caso contrário, você enfrentará à primeira vista inexplicável "SQLException: no driver adequado". Mais uma vez, esse é um erro no driver JDBC, não no seu próprio código. Atualmente, nenhum driver JDBC deve conter esse bug. Então você pode (e deve) deixar de newInstance()fora.

BalusC
fonte
17

1: se você estiver interessado apenas no bloco estático da classe, o carregamento que a classe faria apenas e executaria blocos estáticos, tudo o que você precisa é:

Class.forName("Somthing");

2: se você estiver interessado em carregar a classe, execute seus blocos estáticos e também deseje acessar sua parte não estática, precisará de uma instância e, em seguida:

Class.forName("Somthing").newInstance();
Hussain Akhtar Wahid 'Ghouri'
fonte
Excelente resposta! Claro e conciso!
gaurav
6

Class.forName () obtém uma referência a uma classe, Class.forName (). NewInstance () tenta usar o construtor no-arg da classe para retornar uma nova instância.

Gopi
fonte
3

"Class.forName ()" retorna o tipo de classe para o nome fornecido. "newInstance ()" retorna uma instância desta classe.

No tipo, você não pode chamar diretamente nenhum método de instância, mas pode usar apenas reflexão para a classe. Se você deseja trabalhar com um objeto da classe, é necessário criar uma instância (o mesmo que chamar "new MyClass ()").

Exemplo para "Class.forName ()"

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

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

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

apenas adicionando as respostas acima, quando temos um código estático (ou seja, o bloco de código é independente da instância) que precisa estar presente na memória, podemos ter a classe retornada, portanto, usaremos Class.forname ("someName") caso contrário Se não houver código estático, podemos procurar Class.forname (). newInstance ("someName"), pois ele carregará blocos de código no nível do objeto (não estáticos) na memória

sij
fonte
1

Não importa quantas vezes você chame o método Class.forName (), apenas quando o bloco estático for executado, não várias vezes:

pacote forNameMethodDemo;

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

}

public class DemoClass {

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

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

}

a saída será:

in Static block in Instance block

Esta in Static blockdeclaração é impressa apenas uma vez, não três vezes.

Priyanka Wagh
fonte
0

Class.forName () -> forName () é o método estático da classe Class; ele retorna o objeto de classe Class usado para reflexão e não o objeto de classe do usuário, portanto, você só pode chamar métodos de classe de classe como getMethods (), getConstructors () etc.

Se você se preocupa apenas em executar o bloco estático da sua classe (fornecida em tempo de execução) e obter apenas informações de métodos, construtores, Modificador etc. da sua classe, você pode fazer com esse objeto que você obtém usando Class.forName ()

Mas se você quiser acessar ou chamar seu método de classe (classe que você forneceu em tempo de execução), precisará do objeto para que o método newInstance da classe Class faça isso por você. Crie uma nova instância da classe e devolva-a para você . Você só precisa digitá-lo na sua classe.

ex: suponha que Employee seja sua classe então

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

// args [0] = argumento de linha cmd para dar classe em tempo de execução.

Funcionário ob1 = a.newInstance ();

a.newInstance () é semelhante à criação de um objeto usando new Employee ().

agora você pode acessar todos os campos e métodos visíveis da sua classe.

Vinod Malkani
fonte