Jaka jest różnica między „Class.forName ()” a „Class.forName (). NewInstance ()”?

165

Jaka jest różnica między Class.forName()i Class.forName().newInstance()?

Nie rozumiem istotnej różnicy (coś o nich czytałem!). Czy mógłbyś mi pomóc?

Johanna
źródło

Odpowiedzi:

247

Może przykład pokazujący, jak używane są obie metody, pomoże ci lepiej zrozumieć. Rozważmy więc następującą klasę:

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

Jak wyjaśniono w jego javadoc, wywołanie zwraca obiekt skojarzony z klasą lub interfejsem o podanej nazwie łańcucha, tj. Zwraca zmienną typu, na którą ma to wpływ .Class.forName(String) Classtest.Demo.classclazzClass

Następnie wywołanie tworzy nowe wystąpienie klasy reprezentowanej przez ten obiekt. Klasa jest tworzona tak, jakby była wyrażeniem z pustą listą argumentów. Innymi słowy, jest to w rzeczywistości równoważne a i zwraca nowe wystąpienie .clazz.newInstance() Classnewnew Demo()Demo

Uruchomienie tej Demoklasy powoduje wypisanie następującego wyniku:

Hi!

Duża różnica w stosunku do tradycyjnej newpolega na tym, że newInstanceumożliwia utworzenie instancji klasy, której nie znasz do czasu uruchomienia, dzięki czemu kod jest bardziej dynamiczny.

Typowym przykładem jest interfejs API JDBC, który ładuje w czasie wykonywania dokładny sterownik wymagany do wykonania pracy. Kontenery EJB, kontenery serwletów to inne dobre przykłady: używają dynamicznego ładowania środowiska uruchomieniowego do ładowania i tworzenia komponentów, o których nic nie wiedzą przed uruchomieniem.

Właściwie, jeśli chcesz pójść dalej, spójrz na artykuł Teda Newarda Understanding Class.forName (), który parafrazowałem w akapicie powyżej.

EDYCJA (odpowiadanie na pytanie z PO zamieszczone jako komentarz): Przypadek sterowników JDBC jest nieco wyjątkowy. Jak wyjaśniono w rozdziale DriverManager w podręczniku Pierwsze kroki z interfejsem API JDBC :

(...) DriverKlasa jest ładowana, a zatem automatycznie rejestrowana w programie DriverManager, na jeden z dwóch sposobów:

  1. wywołując metodę Class.forName. To jawnie ładuje klasę sterownika. Ponieważ nie zależy to od żadnej zewnętrznej konfiguracji, ten sposób ładowania sterownika jest zalecany do korzystania z DriverManager frameworka. Poniższy kod ładuje klasę acme.db.Driver:

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

    Jeśli acme.db.Driverzostał napisany w taki sposób, że jego załadowanie powoduje utworzenie instancji, a także wywołanie DriverManager.registerDriverz tą instancją jako parametrem (tak jak powinno), to znajduje się na DriverManagerliście sterowników i jest dostępne do utworzenia połączenia.

  2. (...)

W obu tych przypadkach to nowo załadowana Driverklasa musi zarejestrować się przez wywołanie DriverManager.registerDriver. Jak wspomniano, powinno to być zrobione automatycznie po załadowaniu klasy.

Aby zarejestrować się podczas inicjalizacji, sterownik JDBC zazwyczaj używa statycznego bloku inicjalizacyjnego w następujący sposób:

package acme.db;

public class Driver {

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

    ...
}

Wywołanie Class.forName("acme.db.Driver")powoduje inicjalizację acme.db.Driverklasy, a tym samym wykonanie statycznego bloku inicjalizacyjnego. I Class.forName("acme.db.Driver")rzeczywiście „utworzy” instancję, ale jest to tylko konsekwencja tego, jak (dobry) sterownik JDBC został zaimplementowany.

Na marginesie wspomnę, że wszystko to nie jest już wymagane w JDBC 4.0 (dodanym jako domyślny pakiet od wersji Java 7) i nowej funkcji automatycznego ładowania sterowników JDBC 4.0. Zobacz ulepszenia JDBC 4.0 w Java SE 6 .

Pascal Thivent
źródło
2
na powyższej stronie jest napisane, że: „Calling the Class.forName automatycznie tworzy instancję sterownika i rejestruje ją w DriverManager, więc nie ma potrzeby tworzenia instancji tej klasy. Gdybyś miał utworzyć własną instancję , tworzyłbyś niepotrzebny duplikat, ale nie wyrządziłoby to żadnej szkody ”. oznacza to, że przez Class.forName utworzysz instancję outomatycznie i jeśli chcesz utworzyć drugą, utworzy to niepotrzebną instancję, więc zarówno Calss.forName (), jak i Class.forName (). newInstance () utworzą instancję kierowca!!
Johanna
10
Sterowniki JDBC są „specjalne”, są zapisywane ze statycznym blokiem inicjującym, w którym instancja jest tworzona i przekazywana jako parametr programu DriverManager.registerDriver. Wywołanie Class.forNamesterownika JDBC powoduje jego inicjalizację, a tym samym wykonanie bloku statycznego. Na przykład spójrz na java2s.com/Open-Source/Java-Document/Database-DBMS/… . Jest to więc właściwie szczególny przypadek ze względu na elementy wewnętrzne sterownika.
Pascal Thivent
1
Zauważyłem, że w innej odpowiedzi użycie Class.newInstance () jest zdecydowanie odradzane. Zalecane jest użycie po kolei Class.getConstructor (), a następnie Constructor.newInstance (). Pozwala to uniknąć maskowania możliwych wyjątków.
LS
„newInstance pozwala na utworzenie instancji klasy, której nie znasz do czasu uruchomienia”. Dzięki.
Code Enthusiastic
37

Class.forName () podaje obiekt klasy, który jest przydatny do refleksji. Metody, które ma ten obiekt, są definiowane przez Javę, a nie przez programistę piszącego klasę. Są takie same dla każdej klasy. Wywołanie newInstance () on daje instancję tej klasy (tj. Wywołanie Class.forName("ExampleClass").newInstance()jej jest równoważne wywołaniu new ExampleClass()), na której można wywołać metody zdefiniowane przez klasę, uzyskać dostęp do widocznych pól itp.

Thomas Lötzer
źródło
29

W świecie JDBC normalną praktyką (zgodnie z interfejsem API JDBC) jest Class#forName()ładowanie sterownika JDBC. Sterownik JDBC powinien mianowicie zarejestrować się w DriverManagerstatycznym bloku:

package com.dbvendor.jdbc;

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

public class MyDriver implements Driver {

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

    public MyDriver() {
        //
    }

}

Wywołanie Class#forName()spowoduje wykonanie wszystkich statycznych inicjatorów . W ten sposób DriverManagermoże znaleźć powiązany sterownik wśród zarejestrowanych sterowników według adresu URL połączenia, podczas getConnection()którego z grubsza wygląda to następująco:

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

Ale były też buggy sterowników JDBC, zaczynając org.gjt.mm.mysql.Driverjako dobrze znany przykład, który niepoprawnie rejestruje się wewnątrz konstruktora zamiast statycznego bloku:

package com.dbvendor.jdbc;

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

public class BadDriver implements Driver {

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

}

Jedynym sposobem, aby działał dynamicznie, jest newInstance()późniejsze zadzwonienie ! W przeciwnym razie napotkasz na pierwszy rzut oka niewytłumaczalne „SQLException: brak odpowiedniego sterownika”. Po raz kolejny jest to błąd w sterowniku JDBC, a nie w Twoim własnym kodzie. Obecnie żaden sterownik JDBC nie powinien zawierać tego błędu. Więc możesz (i powinieneś) zostawić to newInstance()daleko.

BalusC
źródło
17

1: jeśli jesteś zainteresowany tylko statycznym blokiem klasy, ładowanie samej klasy wystarczyłoby i wykonałoby statyczne bloki, wszystko czego potrzebujesz to:

Class.forName("Somthing");

2: jeśli jesteś zainteresowany ładowaniem klasy, wykonywaniem jej bloków statycznych, a także chcesz uzyskać dostęp do jej części niestatycznej, potrzebujesz instancji, a następnie potrzebujesz:

Class.forName("Somthing").newInstance();
Hussain Akhtar Wahid „Ghouri”
źródło
Doskonała odpowiedź! Jasne i zwięzłe!
gaurav
6

Class.forName () pobiera odwołanie do Class, Class.forName (). NewInstance () próbuje użyć konstruktora no-arg dla Class, aby zwrócić nową instancję.

Gopi
źródło
3

„Class.forName ()” zwraca typ klasy dla podanej nazwy. „newInstance ()” zwraca instancję tej klasy.

W typie nie można bezpośrednio wywoływać żadnych metod instancji, ale można używać tylko odbicia dla klasy. Jeśli chcesz pracować z obiektem klasy, musisz utworzyć jego instancję (tak samo jak wywołanie „new MyClass ()”).

Przykład dla „Class.forName ()”

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

Przykład dla „Class.forName (). NewInstance ()”

MyClass myClass = (MyClass) Class.forName("test.MyClass").newInstance();
System.out.println("String representation of MyClass instance: " + myClass.toString());
Arne Deutsch
źródło
3

po prostu dodając do powyższych odpowiedzi, gdy mamy kod statyczny (tj. blok kodu jest niezależny od instancji), który musi być obecny w pamięci, możemy zwrócić klasę, więc użyjemy Class.forname ("someName") w przeciwnym razie, jeśli będziemy nie mamy kodu statycznego, możemy przejść do Class.forname (). newInstance ("someName"), ponieważ załaduje do pamięci bloki kodu na poziomie obiektu (nie statyczne)

sij
źródło
1

Bez względu na to, ile razy wywołasz metodę Class.forName (), tylko wtedy, gdy blok statyczny zostanie wykonany nie wielokrotnie:

pakiet forNameMethodDemo;

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

}

klasa publiczna DemoClass {

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

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

}

wynik będzie:

in Static block in Instance block

To in Static blockoświadczenie jest drukowane tylko raz, a nie trzy razy.

Priyanka Wagh
źródło
0

Class.forName () -> forName () to statyczna metoda klasy Class, która zwraca obiekt klasy Class używany do odbicia, a nie obiekt klasy użytkownika, więc możesz wywołać na nim tylko metody klasy Class, takie jak getMethods (), getConstructors () itp.

Jeśli zależy Ci na uruchamianiu tylko statycznego bloku swojej (podanej w Runtime) klasie i pobieraniu tylko informacji o metodach, konstruktorach, modyfikatorze itp. Swojej klasy, możesz zrobić z tym obiektem, który otrzymujesz za pomocą Class.forName ()

Ale jeśli chcesz uzyskać dostęp lub wywołać swoją metodę klasy (klasę, którą podałeś w czasie wykonywania), musisz mieć jej obiekt, więc newInstance metoda klasy Class zrobi to za Ciebie. Utworzy nową instancję klasy i zwróci ci ją Wystarczy, że prześlesz to na swoją klasę.

ex-: przypuśćmy, że pracownik jest wtedy twoją klasą

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

// args [0] = argument linii cmd do nadania klasie w czasie wykonywania.

Pracownik ob1 = a.newInstance ();

a.newInstance () jest podobne do tworzenia obiektu przy użyciu funkcji new Employee ().

teraz możesz uzyskać dostęp do wszystkich widocznych pól i metod swojej klasy.

Vinod Malkani
źródło