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

165

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

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

Johanna
источник

Ответы:

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 ()», которую я перефразировал в параграфе чуть выше.

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

(...) 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/… для примера. Так что это на самом деле частный случай из-за внутренних компонентов драйвера.
Паскаль Тивент
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хорошо известного примера, который некорректно регистрируется внутри Конструктора вместо статического блока:

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()прочь.

BalusC
источник
17

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

Class.forName("Somthing");

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

Class.forName("Somthing").newInstance();
Хуссейн Ахтар Вахид Гури
источник
Отличный ответ! Ясно и сжато!
Гаурав
6

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

Гопи
источник
3

«Class.forName ()» возвращает Class-Type для данного имени. «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());
Арне Дойч
источник
3

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

Sij
источник
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 () и т. Д.

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

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

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

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

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

Employee ob1 = a.newInstance ();

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

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

Винод Малкани
источник