Яка різниця між "Class.forName ()" і "Class.forName (). NewInstance ()"?


165

У чому різниця між Class.forName()і Class.forName().newInstance()?

Я не розумію суттєвої різниці (я щось читав про них!). Чи можете ви мені допомогти?

Відповіді:


247

Можливо, приклад, що демонструє, як використовуються обидва методи, допоможе вам зрозуміти речі краще. Отже, розглянемо наступний клас:

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

Як пояснено в його javadoc, виклик повертає об'єкт, пов'язаний з класом або інтерфейсом із заданим іменем рядка, тобто повертається, на який впливає змінна типуClass.forName(String) Classtest.Demo.classclazzClass .

Тоді виклик створює новий екземпляр класу, представленого цим об'єктом. Клас інстанціюється як би виразом із порожнім списком аргументів. Іншими словами, це фактично еквівалентно a і повертає новий екземпляр .clazz.newInstance() Classnewnew Demo()Demo

І запуск цього Demoкласу друкує такий результат:

Hi!

Велика відмінність від традиційного newполягає в тому, що newInstanceдозволяє створити екземпляр класу, який ви не знаєте до часу виконання, зробивши ваш код більш динамічним.

Типовий приклад - API JDBC, який під час виконання завантажує точний драйвер, необхідний для виконання роботи. Контейнери EJB, контейнери сервлетів - це інші хороші приклади: вони використовують динамічне завантаження для виконання для завантаження та створення компонентів, про які вони нічого не знають до часу виконання.

Насправді, якщо ви хочете піти далі, подивіться на папір Теда Ньюарда Розуміння Class.forName (), який я перефразовував у параграфі вище.

EDIT (відповідаючи на запитання ОП, розміщений як коментар): Випадок драйверів JDBC дещо особливий. Як пояснюється в DriverManager чолі Початок роботи з API JDBC :

(...) DriverКлас завантажується і тому автоматично реєструється у DriverManager, одним із двох способів:

  1. викликавши метод Class.forName. Це явно завантажує водійський клас. Оскільки це не залежить від будь-яких зовнішніх налаштувань, цей спосіб завантаження драйвера є рекомендованим для використання DriverManager фреймворку. Наступний код завантажує клас acme.db.Driver:

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

    Якщо acme.db.Driverбуло написано так, що завантаження викликає створення екземпляра, а також викликає DriverManager.registerDriverцей екземпляр як параметр (як це слід робити), то він знаходиться у DriverManagerсписку драйверів і доступний для створення з'єднання.

  2. (...)

В обох цих випадках обов'язок новозавантаженого Driverкласу є зареєструватися, зателефонувавши DriverManager.registerDriver. Як було сказано, це потрібно робити автоматично, коли клас завантажується.

Щоб зареєструватися під час ініціалізації, драйвер JDBC зазвичай використовує статичний блок ініціалізації таким чином:

package acme.db;

public class Driver {

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

    ...
}

Виклик Class.forName("acme.db.Driver")викликає ініціалізацію acme.db.Driverкласу і, таким чином, виконання блоку статичної ініціалізації. І Class.forName("acme.db.Driver")справді "створить" екземпляр, але це лише наслідок того, як (хороший) драйвер JDBC реалізований.

Як зауваження, я зазначу, що все це більше не потрібно для JDBC 4.0 (додається як пакет за замовчуванням з Java 7) та новою функцією автоматичного завантаження драйверів JDBC 4.0. Дивіться вдосконалення JDBC 4.0 в Java SE 6 .


але все-таки на цьому веб-сайті: java.sun.com/docs/books/tutorial/jdbc/basics/connecting.html
Йоганна

2
на вищезгаданому сайті написано, що: "Виклик Class.forName автоматично створює екземпляр драйвера та реєструє його за допомогою DriverManager, тому вам не потрібно створювати екземпляр класу. Якщо б ви створили свій власний екземпляр , ви створили б непотрібний дублікат, але це не принесе шкоди ". це означає, що за допомогою Class.forName ви створите екземпляр зовнішньо, і якщо ви хочете створити інший, це зробить непотрібний екземпляр. Отже, і Calss.forName (), і Class.forName (). newInstance () створить екземпляр водій !!
Йоганна

10
Драйвери JDBC є "спеціальними", вони записуються зі статичним блоком ініціалізації, де створюється екземпляр і передається як параметр DriverManager.registerDriver. Виклик Class.forNameдрайвера JDBC викликає його ініціалізацію і, таким чином, виконання статичного блоку. Погляньте на приклад java2s.com/Open-Source/Java-Document/Database-DBMS/… . Тож це насправді особливий випадок через внутрішність водія.
Паскаль Thivent

1
Я помітив, що в іншій відповіді використання Class.newInstance () сильно не рекомендується. Рекомендується використовувати Class.getConstructor (), а потім по черзі Constructor.newInstance (). Це дозволяє уникнути маскування можливих винятків.
LS

"newInstance дозволяє створити екземпляр класу, про який ви не знаєте, поки час виконання" зробив мій день. Дякую.
Захоплений Кодексом

37

Class.forName () дає вам об’єкт класу, який корисний для роздумів. Методи, якими володіє цей об’єкт, визначаються Java, а не програміст, що пише клас. Вони однакові для кожного класу. Виклик newInstance () дає вам екземпляр цього класу (тобто виклик Class.forName("ExampleClass").newInstance()його еквівалентний виклику new ExampleClass()), за допомогою якого ви можете викликати методи, які визначає клас, отримати доступ до видимих ​​полів тощо.


29

У світі JDBC звичайною практикою (згідно API JDBC) є те, що ви використовуєте Class#forName()для завантаження драйвера JDBC. Драйвер JDBC повинен саме зареєструватися у DriverManagerстатичному блоці:

package com.dbvendor.jdbc;

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

public class MyDriver implements Driver {

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

    public MyDriver() {
        //
    }

}

Виклик Class#forName()виконає всі статичні ініціалізатори . Таким чином, DriverManagerможна знайти пов'язаного драйвера серед зареєстрованих драйверів за URL-адресою підключення, протягом getConnection()якого приблизно так виглядають:

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

Але також були помилкові драйвери JDBC, починаючи з org.gjt.mm.mysql.Driverдобре відомого прикладу, який неправильно реєструється всередині Constructor замість статичного блоку:

package com.dbvendor.jdbc;

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

public class BadDriver implements Driver {

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

}

Єдиний спосіб змусити його динамічно працювати - дзвонити newInstance()згодом! Інакше ви зіткнетесь з першого погляду незрозумілою "SQLException: немає відповідного драйвера". Ще раз, це помилка у драйвері JDBC, а не у власному коді. На сьогодні жоден драйвер JDBC не повинен містити цю помилку. Тож ви можете (і повинні) залишити newInstance()подалі.


17

1: якщо вас цікавить лише статичний блок класу, завантаження класу буде виконуватись і виконає статичні блоки, тоді все, що вам потрібно, це:

Class.forName("Somthing");

2: якщо вам цікаво завантажувати клас, виконувати його статичні блоки, а також хочете отримати доступ до його нестатичної частини, то вам потрібен екземпляр і тоді вам потрібно:

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

Відмінна відповідь! Чітко і стисло!
gaurav

6

Class.forName () отримує посилання на Class, Class.forName (). NewInstance () намагається використовувати конструктор no-arg для класу для повернення нового екземпляра.


3

"Class.forName ()" повертає Class-Type для даного імені. "newInstance ()" повертає екземпляр цього класу.

У тому типі ви не можете викликати безпосередньо будь-які методи екземпляра, але можете використовувати лише відображення для класу. Якщо ви хочете працювати з об'єктом класу, вам слід створити його примірник (те саме, що викликати "новий MyClass ()").

Приклад для "Class.forName ()"

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

Приклад для "Class.forName (). NewInstance ()"

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

3

просто додаючи до вищезазначених відповідей, коли у нас є статичний код (тобто блок коду не залежить від примірника), який повинен бути присутнім у пам'яті, ми можемо повернути клас, тому ми будемо використовувати Class.forname ("someName") ще, якщо ми у вас немає статичного коду, ми можемо перейти до Class.forname (). newInstance ("someName"), оскільки він буде завантажувати блоки об'єктного коду (нестатичні) в пам'ять


1

Незалежно від того, скільки разів ви викликаєте метод Class.forName (), лише один раз статичний блок виконується не кілька разів:

пакет дляNameMethodDemo;

загальнодоступний клас 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 {

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

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

}

вихід буде:

in Static block in Instance block

Ця in Static blockзаява друкується лише один раз, а не три рази.


0

Class.forName () -> forName () - це статичний метод класу Class, він повертає об’єкт класу Class, який використовується для відображення не об'єкта класу користувача, тому ви можете викликати на ньому лише методи класу Class, як getMethods (), getConstructors () тощо.

Якщо ви дбаєте лише про те, щоб запускати статичний блок вашого класу (Runtime date) і лише отримувати інформацію про методи, конструктори, модифікатор тощо вашого класу, ви можете робити з цим об'єктом, який ви отримуєте за допомогою Class.forName ()

Але якщо ви хочете отримати доступ або зателефонувати до свого методу класу (класу, який ви дали під час виконання), тоді вам потрібно мати його об'єкт, щоб метод newInstance клас класу зробив це для вас. Створіть новий екземпляр класу і поверніть його вам . Вам просто потрібно набрати його для свого класу.

Наприклад: припустимо, працівник тоді ваш клас

Клас a = Class.forName (args [0]);

// args [0] = аргумент рядка cmd для надання класу під час виконання.

Співробітник ob1 = a.newInstance ();

a.newInstance () подібний до створення об'єкта за допомогою нового Employee ().

тепер ви можете отримати доступ до всіх видимих ​​полів та методів вашого класу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.