В чем разница между "Class.forName()" и "Class.forName(). NewInstance()"?

В чем разница между Class.forName() а также Class.forName().newInstance()?

Я не понимаю существенной разницы (я что-то о них читал!). Не могли бы вы мне помочь?

8 ответов

Решение

Возможно, пример, демонстрирующий, как используются оба метода, поможет вам лучше понять вещи. Итак, рассмотрим следующий класс:

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) возвращает Class объект, связанный с классом или интерфейсом с заданным именем строки, т.е. он возвращает test.Demo.class который влияет на clazz переменная типа Class,

Затем, позвонив clazz.newInstance() создает новый экземпляр класса, представленного этим Class объект. Класс создается как будто new выражение с пустым списком аргументов. Другими словами, это здесь фактически эквивалентно new Demo() и возвращает новый экземпляр Demo,

И работает это Demo Таким образом, класс выводит следующий вывод:

Hi!

Большая разница с традиционным new в том, что newInstance позволяет создать экземпляр класса, который вы не знаете до времени выполнения, что делает ваш код более динамичным.

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

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

РЕДАКТИРОВАТЬ (отвечая на вопрос из ОП, опубликованных в качестве комментария): Случай с драйверами JDBC немного особенный. Как объясняется в главе DriverManager Начало работы с JDBC API:

(...) A 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.

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

В мире JDBC обычная практика (согласно JDBC API) заключается в том, что вы используете 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 а также хорошо известный пример, который некорректно регистрируется внутри Конструктора вместо статического блока:

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() далеко.

1: если вас интересует только статический блок класса, загрузка класса будет выполняться только и будет выполняться статические блоки, тогда все, что вам нужно:

Class.forName("Somthing");

2: если вы заинтересованы в загрузке класса, выполнении его статических блоков, а также хотите получить доступ к его не статической части, тогда вам нужен экземпляр, а затем вам нужно:

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

Class.forName() получает ссылку на Class, Class.forName(). NewInstance() пытается использовать конструктор no-arg, чтобы Class возвращал новый экземпляр.

"Class.forName()" возвращает тип класса для данного имени. "newInstance()" возвращает экземпляр этого класса.

Для типа вы не можете напрямую вызывать методы экземпляра, но можете использовать только отражение для класса. Если вы хотите работать с объектом класса, вы должны создать его экземпляр (аналогично вызову "new 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());

Просто добавляя к ответам выше, когда у нас есть статический код (т. е. блок кода не зависит от экземпляра), который должен присутствовать в памяти, мы можем вернуть класс, поэтому мы будем использовать Class.forname("someName") в противном случае, если мы у нас нет статического кода, мы можем использовать Class.forname (). newInstance("someName"), поскольку он будет загружать блоки кода уровня объекта (не статические) в память

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

пакет 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();
    }

}

public class DemoClass {

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

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

}

вывод будет:

in Static block in Instance block

Этот in Static block выписка печатается только один раз, а не трижды.

Class.forName() ->forName() - статический метод класса Class, который возвращает объект класса Class, используемый для отражения, а не объект класса пользователя, поэтому вы можете вызывать только методы класса Class для него, такие как getMethods (), getConstructors () и т. Д.

Если вы заботитесь только о запуске статического блока вашего (заданного во время выполнения) класса и только о получении информации о методах, конструкторах, модификаторах и т. Д. Вашего класса, вы можете делать с этим объектом, который вы получаете с помощью Class.forName()

Но если вы хотите получить доступ к своему методу класса или вызвать его (класс, который вы указали во время выполнения), вам нужно иметь его объект, чтобы метод newInstance класса Class сделал это для вас. Он создает новый экземпляр класса и возвращает его вам.. Вам просто нужно привести его к своему классу.

ex-: предположим, сотрудник тогда ваш класс

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

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

Сотрудник ob1 = a.newInstance ();

a.newInstance () аналогичен созданию объекта с использованием нового Employee().

Теперь вы можете получить доступ ко всем видимым полям и методам вашего класса.

Другие вопросы по тегам