Ответ
Нет никакой функциональной разницы. Как вы обнаружили, существуют различные способы получения объекта Class
и создания экземпляров этого класса. Однако все они приводят к одному и тому же результату.
В общем, до тех пор, пока не потребуется, всегда:
- Используйте литералы класса или
getClass()
для получения Class
- Пример 1:
Class<Foo> cl = Foo.class;
- Пример 2:
Class<? extends Foo> cl = fooInstance.getClass();
- Используйте ключевое слово
new
для создания экземпляров
- Пример:
Foo f = new Foo();
- Предупреждение: Иногда API разрабатывается с использованием Pattern Builder , Factory Method Pattern и т. Д. ... Если это так, то вам придется использовать эти методы. Внутренне, методы построения и фабрики могут даже использовать ключевое слово
new
.
Пояснения и незначительные (?) Различия
Классы
На макушке головы я думаю, как можно получить Class
объект:
- Использование литералов класса
Class<Foo> cl = Foo.class;
- Вызов
getClass()
в экземпляре
Class<? extends Foo> cl = fooInstance.getClass();
- Вызов
Class.forName(String)
Class<?> cl = Class.forName("some.package.Foo");
- Это сокращение от
Class.forName("some.package.Foo", true, currentClassLoader)
- Звонок
ClassLoader.loadClass(String)
Class<?> cl = classLoader.loadClass("some.package.Foo");
- Не обязательно загрузить
Class
. Если Class
уже загружен, этот загруженный экземпляр будет возвращен.
Все вышеперечисленное получит объект Class
, представляющий some.package.Foo
. По всей вероятности (я не 100% уверен), методы 1, 2 и 3 все в конечном итоге в конечном итоге делегируют методу 4.
Вы заметите, что общие подписи объектов Class
(части <>
) различаются в зависимости от способа получения Class
. Методы 1 и 2 знают, какой тип будет Class
во время компиляции, и поэтому могут возвращать Class
с соответствующими обобщениями. Тогда как методы 3 и 4 не имеют представления о том, какой тип Class
будет представлять во время выполнения, и поэтому возвращают Class<?>
(?
является подстановочным знаком).
Что следует отметить в отношении метода 3. Как я упоминал выше, Class.forName(String)
является сокращением для Class.forName(String, boolean, ClassLoader)
, где boolean
будет true
, а ClassLoader
будет текущим ClassLoader
. Параметр boolean
определяет, будет ли инициализирован Class
. Инициализация класса означает (среди прочего?) Инициализацию всех переменных static
и запуск инициализаторов static
. Таким образом, в то время как методы 1, 2 и 4 будут не инициализировать Class
, метод 3 будет . Если вы не хотите, чтобы метод 3 инициализировал Class
, вам нужно будет использовать более длинную версию и задать параметр boolean
false
.
Ссылка в комментариях к вопросу говорит о том, почему вы должны использовать методы 3 или 4.
Создание экземпляров
Опять же, не в своей голове, я могу подумать о том, как создавать объекты:
- Использование ключевого слова
new
- Использование
Class.newInstance()
Foo f = fooClass.newInstance();
- Требуется, чтобы класс имел конструктор без аргументов
- устарело с Java 9 в пользу использования
Constructor
объектов
- Использование одного из объектов
Constructor
Foo f = fooClass.getConstructor().newInstance();
Основное отличие здесь в том, как каждый метод создает экземпляр. Первый метод просто использует ключевое слово new
. 2-й и 3-й методы используют отражение . Отражение полезно, когда вы не знаете типы во время компиляции, но его следует избегать до тех пор, пока оно не понадобится.
В методе 3 используется Class.getConstructor(Class<?>... paramterTypes)
. Поскольку я передаю пустой массив типов параметров, возвращаемый Constructor
является конструктором без аргументов. Это не получится, если у класса нет конструктора без аргументов.
Использование getDeclaredConstructors()
просто возвращает всех конструкторов, а затем вы выбираете тот, который вам нужен, и вызываете newInstance
. Пример, который я привел в 3, обходит это, перейдя прямо к общедоступному конструктору без аргументов. Использование getDeclaredConstructors()
также даст вам непубличные конструкторы (т.е. protected
, package
и private
). Это может позволить вам вызывать непубличный конструктор, который вы бы не смогли. Но это только если у вас есть доступ для вызова конструктора; вам понадобятся такие вещи, как разрешение от любого установленного SecurityManager
и (если используется Java 9+) пакет, в котором находится класс, должен быть рефлексивно доступным (opens
) для вашего модуля.
Некоторые ссылки