Was ist der Unterschied zwischen "Class.forName ()" und "Class.forName (). NewInstance ()"?

165

Was ist der Unterschied zwischen Class.forName()und Class.forName().newInstance()?

Ich verstehe den signifikanten Unterschied nicht (ich habe etwas darüber gelesen!). Kannst du mir bitte Helfen?

Johanna
quelle

Antworten:

247

Vielleicht hilft Ihnen ein Beispiel, das zeigt, wie beide Methoden verwendet werden, die Dinge besser zu verstehen. Betrachten Sie also die folgende Klasse:

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

Wie in seinem Javadoc erläutert, gibt der Aufruf das Objekt zurück, das der Klasse oder Schnittstelle mit dem angegebenen Zeichenfolgennamen zugeordnet ist, dh es wird zurückgegeben, was für die Variable vom Typ betroffen ist .Class.forName(String) Classtest.Demo.classclazzClass

Durch das Aufrufen wird dann eine neue Instanz der Klasse erstellt, die von diesem Objekt dargestellt wird. Die Klasse wird wie durch einen Ausdruck mit einer leeren Argumentliste instanziiert . Mit anderen Worten, dies ist hier tatsächlich äquivalent zu a und gibt eine neue Instanz von zurück .clazz.newInstance() Classnewnew Demo()Demo

Wenn Sie diese DemoKlasse ausführen, wird die folgende Ausgabe gedruckt:

Hi!

Der große Unterschied zum herkömmlichen System newbesteht darin, dass newInstanceeine Klasse, die Sie erst zur Laufzeit kennen, instanziiert werden kann, wodurch Ihr Code dynamischer wird.

Ein typisches Beispiel ist die JDBC-API, die zur Laufzeit den genauen Treiber lädt, der für die Ausführung der Arbeit erforderlich ist. EJBs-Container und Servlet-Container sind weitere gute Beispiele: Sie verwenden das dynamische Laden zur Laufzeit, um Komponenten zu laden und zu erstellen, von denen sie vor der Laufzeit nichts wissen.

Wenn Sie noch weiter gehen möchten, werfen Sie einen Blick auf Ted Newards Artikel Understanding Class.forName () , den ich im obigen Absatz umschrieben habe.

BEARBEITEN (Beantwortung einer Frage aus dem OP als Kommentar): Der Fall der JDBC-Treiber ist etwas Besonderes. Wie im DriverManager- Kapitel unter Erste Schritte mit der JDBC-API erläutert :

(...) Eine DriverKlasse wird DriverManagerauf zwei Arten geladen und daher automatisch bei der registriert :

  1. durch Aufrufen der Methode Class.forName. Dadurch wird die Treiberklasse explizit geladen. Da dies nicht von einem externen Setup abhängt, wird diese Art des Ladens eines Treibers für die Verwendung des DriverManager Frameworks empfohlen . Der folgende Code lädt die Klasse acme.db.Driver:

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

    Wenn dies acme.db.Driverso geschrieben wurde, dass beim Laden eine Instanz erstellt wird und DriverManager.registerDriverdiese Instanz auch als Parameter aufgerufen wird (wie dies der Fall sein sollte), befindet sie sich in der DriverManagerTreiberliste und steht zum Erstellen einer Verbindung zur Verfügung.

  2. (...)

In beiden Fällen liegt es in der Verantwortung der neu geladenen DriverKlasse, sich durch Aufruf zu registrieren DriverManager.registerDriver. Wie bereits erwähnt, sollte dies automatisch erfolgen, wenn die Klasse geladen wird.

Um sich während der Initialisierung zu registrieren, verwendet der JDBC-Treiber normalerweise einen statischen Initialisierungsblock wie folgt:

package acme.db;

public class Driver {

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

    ...
}

Der Aufruf Class.forName("acme.db.Driver")bewirkt die Initialisierung der acme.db.DriverKlasse und damit die Ausführung des statischen Initialisierungsblocks. Und Class.forName("acme.db.Driver")wird in der Tat eine Instanz "erstellen", aber dies ist nur eine Folge davon, wie (gute) JDBC-Treiber implementiert werden.

Als Randnotiz möchte ich erwähnen, dass all dies mit JDBC 4.0 (seit Java 7 als Standardpaket hinzugefügt) und der neuen Funktion zum automatischen Laden von JDBC 4.0-Treibern nicht mehr erforderlich ist. Siehe JDBC 4.0-Verbesserungen in Java SE 6 .

Pascal Thivent
quelle
aber immer noch auf dieser Seite: java.sun.com/docs/books/tutorial/jdbc/basics/connecting.html
Johanna
2
Auf der obigen Site heißt es: "Durch Aufrufen von Class.forName wird automatisch eine Instanz eines Treibers erstellt und beim DriverManager registriert, sodass Sie keine Instanz der Klasse erstellen müssen. Wenn Sie eine eigene Instanz erstellen möchten Sie würden ein unnötiges Duplikat erstellen, aber es würde keinen Schaden anrichten. " Dies bedeutet, dass Sie mit Class.forName eine Instanz outomatisch erstellen. Wenn Sie die andere erstellen möchten, wird eine unnötige Instanz erstellt. Sowohl Calss.forName () als auch Class.forName (). newInstance () erstellen eine Instanz von Treiber!!
Johanna
10
JDBC-Treiber sind "speziell". Sie werden mit einem statischen Initialisierungsblock geschrieben, in dem eine Instanz erstellt und als Parameter von übergeben wird DriverManager.registerDriver. Das Aufrufen Class.forNameeines JDBC-Treibers bewirkt dessen Initialisierung und damit die Ausführung des statischen Blocks. Werfen Sie einen Blick auf java2s.com/Open-Source/Java-Document/Database-DBMS/... für ein Beispiel. Dies ist also tatsächlich ein besonderer Fall aufgrund von Fahrerinternen.
Pascal Thivent
1
Ich habe festgestellt, dass in einer anderen Antwort die Verwendung von Class.newInstance () dringend empfohlen wird. Es wird empfohlen, Class.getConstructor () zu verwenden, gefolgt von Constructor.newInstance (). Es wird vermieden, mögliche Ausnahmen zu maskieren.
LS
"newInstance ermöglicht das Instanziieren einer Klasse, die Sie erst zur Laufzeit kennen", war mein Tag. Vielen Dank.
Code Enthusiastic
37

Class.forName () gibt Ihnen das Klassenobjekt, das für die Reflexion nützlich ist. Die Methoden dieses Objekts werden von Java definiert, nicht vom Programmierer, der die Klasse schreibt. Sie sind für jede Klasse gleich. Wenn Sie newInstance () aufrufen, erhalten Sie eine Instanz dieser Klasse (dh das Aufrufen Class.forName("ExampleClass").newInstance()entspricht dem Aufrufen new ExampleClass()), auf der Sie die von der Klasse definierten Methoden aufrufen, auf die sichtbaren Felder zugreifen usw. können.

Thomas Lötzer
quelle
29

In der JDBC-Welt wird normalerweise (gemäß der JDBC-API) Class#forName()ein JDBC-Treiber geladen. Der JDBC-Treiber sollte sich nämlich in DriverManagereinem statischen Block registrieren :

package com.dbvendor.jdbc;

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

public class MyDriver implements Driver {

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

    public MyDriver() {
        //
    }

}

Beim Aufrufen Class#forName()werden alle statischen Initialisierer ausgeführt . Auf diese Weise DriverManagerkann der zugehörige Treiber unter den registrierten Treibern anhand der Verbindungs-URL gefunden werden, getConnection()die ungefähr wie folgt aussieht:

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

Aber es gab auch Buggy JDBC - Treiber, mit dem Start org.gjt.mm.mysql.Driverals bekanntes Beispiel, das sich falsch innerhalb der Register - Konstruktor anstelle eines statischen Block:

package com.dbvendor.jdbc;

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

public class BadDriver implements Driver {

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

}

Die einzige Möglichkeit, es dynamisch zum Laufen zu bringen, besteht darin, newInstance()danach anzurufen ! Andernfalls werden Sie auf den ersten Blick unerklärlichen "SQLException: kein geeigneter Fahrer" gegenüberstehen. Dies ist wiederum ein Fehler im JDBC-Treiber, nicht in Ihrem eigenen Code. Heutzutage sollte kein JDBC-Treiber diesen Fehler enthalten. Sie können (und sollten) also das newInstance()weglassen.

BalusC
quelle
17

1: Wenn Sie nur an dem statischen Block der Klasse interessiert sind, würde das Laden der Klasse nur ausreichen und statische Blöcke ausführen, dann brauchen Sie nur:

Class.forName("Somthing");

2: Wenn Sie daran interessiert sind, die Klasse zu laden, ihre statischen Blöcke auszuführen und auch auf ihren nicht statischen Teil zugreifen möchten, benötigen Sie eine Instanz und dann:

Class.forName("Somthing").newInstance();
Hussain Akhtar Wahid 'Ghouri'
quelle
Hervorragende Antwort! Klar und prägnant!
Gaurav
6

Class.forName () erhält einen Verweis auf eine Klasse, Class.forName (). NewInstance () versucht, den Konstruktor no-arg für die Klasse zu verwenden, um eine neue Instanz zurückzugeben.

Gopi
quelle
3

"Class.forName ()" gibt den Klassentyp für den angegebenen Namen zurück. "newInstance ()" gibt eine Instanz dieser Klasse zurück.

Für den Typ können Sie keine Instanzmethoden direkt aufrufen, sondern nur die Reflektion für die Klasse verwenden. Wenn Sie mit einem Objekt der Klasse arbeiten möchten, müssen Sie eine Instanz davon erstellen (wie beim Aufrufen von "new MyClass ()").

Beispiel für "Class.forName ()"

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

Beispiel für "Class.forName (). NewInstance ()"

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

Wenn wir nur einen statischen Code hinzufügen (dh der Codeblock ist instanzunabhängig), der im Speicher vorhanden sein muss, können wir die Klasse zurückgeben, sodass wir Class.forname ("someName") verwenden, wenn wir dies tun Wenn Sie keinen statischen Code haben, können Sie Class.forname (). newInstance ("someName") verwenden, da Codeblöcke auf Objektebene (nicht statisch) in den Speicher geladen werden

sij
quelle
1

Unabhängig davon, wie oft Sie die Methode Class.forName () aufrufen, wird der statische Block nur einmal ausgeführt, nicht mehrmals:

package forNameMethodDemo;

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

}}

öffentliche Klasse DemoClass {

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

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

}}

Ausgabe wird sein:

in Static block in Instance block

Diese in Static blockErklärung wird nur einmal und nicht dreimal gedruckt.

Priyanka Wagh
quelle
0

Class.forName () -> forName () ist die statische Methode der Klassenklasse. Sie gibt das Klassenklassenobjekt zurück, das für die Reflexion verwendet wird, nicht das Benutzerklassenobjekt. Sie können also nur Klassenklassenmethoden wie getMethods (), getConstructors () usw. Aufrufen.

Wenn Sie nur den statischen Block Ihrer (zur Laufzeit angegebenen) Klasse ausführen und nur Informationen zu Methoden, Konstruktoren, Modifikatoren usw. Ihrer Klasse abrufen möchten, können Sie mit diesem Objekt arbeiten, das Sie mit Class.forName () erhalten.

Wenn Sie jedoch auf Ihre Klassenmethode (Klasse, die Sie zur Laufzeit angegeben haben) zugreifen oder diese aufrufen möchten, muss das Objekt so sein, dass die newInstance-Methode der Klassenklasse dies für Sie erledigt. Sie erstellt eine neue Instanz der Klasse und gibt sie an Sie zurück Sie müssen es nur in Ihre Klasse tippen.

Beispiel: Angenommen, der Mitarbeiter ist dann Ihre Klasse

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

// args [0] = cmd-Zeilenargument zur Angabe der Klasse zur Laufzeit.

Mitarbeiter ob1 = a.newInstance ();

a.newInstance () ähnelt dem Erstellen eines Objekts mit new Employee ().

Jetzt können Sie auf alle sichtbaren Felder und Methoden Ihrer Klasse zugreifen.

Vinod Malkani
quelle