В чем разница между loadClass (("полное имя класса") и <ClassName>.class.getDeclaredConstructors - PullRequest
0 голосов
/ 01 мая 2018
class testMe{

    void show(){
        System.out.println("Hello");
    }

}

public class ClassloadersExample {
    public static void main(String args[]) {
        ClassLoader c = ClassloadersExample.class.getClassLoader(); //Line 1
        try {
            Class c1 = c.loadClass("test.testMe"); // Line 2
            Constructor a[] = c1.getDeclaredConstructors(); 
            for (Constructor constructor : a) {
                testMe m = (testMe)constructor.newInstance();
                m.show();
            }

            Constructor con[] = testMe.class.getDeclaredConstructors(); // Line 6
            for (Constructor constructor : con) {
                constructor.setAccessible(true);
                testMe t = (testMe)constructor.newInstance();
                t.show();
            }
        }
        catch(Exception e){
            System.out.println("exception");
        }

    }
}

Я тестирую приведенный выше код. Оба дают мне одинаковый результат. Я пытаюсь понять разницу между строкой 1,2 и строкой 6. При обоих подходах я могу достичь одного и того же результата.

1 Ответ

0 голосов
/ 01 мая 2018

Ответ

Нет никакой функциональной разницы. Как вы обнаружили, существуют различные способы получения объекта 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 объект:

  1. Использование литералов класса
    • Class<Foo> cl = Foo.class;
  2. Вызов getClass() в экземпляре
    • Class<? extends Foo> cl = fooInstance.getClass();
  3. Вызов Class.forName(String)
    • Class<?> cl = Class.forName("some.package.Foo");
    • Это сокращение от Class.forName("some.package.Foo", true, currentClassLoader)
  4. Звонок 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.

Создание экземпляров

Опять же, не в своей голове, я могу подумать о том, как создавать объекты:

  1. Использование ключевого слова new
    • Foo f = new Foo();
  2. Использование Class.newInstance()
    • Foo f = fooClass.newInstance();
    • Требуется, чтобы класс имел конструктор без аргументов
    • устарело с Java 9 в пользу использования Constructor объектов
  3. Использование одного из объектов 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) для вашего модуля.

Некоторые ссылки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...