Объяснение отражения Java

Java задняя часть JVM API

Рефлексия в Java — важная часть знаний, вы увидите отражение во многих местах. Он предоставляет языку Java возможность загружать, обнаруживать и использовать классы во время выполнения, которые совершенно неизвестны во время компиляции. Эта возможность очень распространена при написании фреймворков, таких как динамические прокси, сканирование классов и синтаксический анализ.

Определение и роль отражения

Механизм отражения: то есть язык Java имеет возможность самопросмотра во время выполнения, который может понять собственную ситуацию и подготовиться к следующему действию. Реакция заключается в том, что во время выполнения для класса мы можем знать, какие методы и свойства есть у класса. Для объекта мы можем вызвать любой из его методов. Это возможность динамически получать информацию о классе и динамически вызывать методы объекта.

Реализация основ отражения

Java предоставляет механизм отражения, основанный на классе Class и библиотеке классов java.lang.reflect. Его основные классы следующие:

  1. Класс: представляет класс или интерфейс.
  2. Поле: представляет переменную-член в классе
  3. Метод: представляет метод в классе.
  4. Конструктор: представляет конструктор класса.
  5. Массив: этот класс предоставляет статические методы для динамического создания массивов и доступа к элементам массива.

Class

Класс Класс — это соответствующий класс в Java, используемый для представления информации о типе среды выполнения. Фактически каждый класс в Java имеет объект класса, и каждый раз, когда мы пишем и компилируем вновь созданный класс, соответствующая информация записывается в файл .class. Когда мы создаем новый объект или обращаемся к статической переменной-члену, подсистема загрузчика классов в JVM загрузит соответствующий объект класса в JVM, а затем JVM создаст нужный нам объект экземпляра или предоставит статическую информацию на основе класса. объект, связанный с информацией этого типа.Ссылочное значение переменной. Мы можем вызвать класс Class как тип класса, а объект Class как объект типа класса (см. Thinking in Java).

Класс Class имеет следующие характеристики:

  1. Класс Класс также является типом класса, а класс — это ключевое слово.
  2. Класс Class имеет только один частный конструктор, и только JVM может создать экземпляр класса Class.
  3. Для объектов одного и того же класса в JVM существует только один соответствующий экземпляр Class для описания информации о его типе. (Тот же класс: то есть имя пакета + имя класса одно и то же, и он загружается одним и тем же загрузчиком классов)

Файл .class хранит всю информацию о классе, такую ​​как все методы, все конструкторы, все поля (свойства-члены) и так далее. При запуске JVM соответствующие классы загружаются в память через файл .class Процесс выглядит следующим образом:

)

Как получить экземпляр класса

Как упоминалось выше, класс Class имеет только один закрытый конструктор. Таким образом, вы не можете получить экземпляр класса с помощью нового метода.

/*
 * Constructor. Only the Java Virtual Machine creates Class
 * objects.
 */
private Class() {}
Метод Class.forName()

Вы можете получить экземпляр класса с помощью метода forName класса, где имя класса должно указывать полный путь к классу. Этот метод можно использовать только для получения объектов типа класса ссылочного типа.

// 这种方式会使用当前的类的加载器加载,并且会将 Class 类实例初始化
Class<?> clazz = Class.forName("java.lang.String");
// 上面的调用方式等价于
Class<?> clazz = Class.forName("java.lang.String", true, currentLoader);

Использование этого метода может вызвать исключение ClassNotFoundException, которое возникает на этапе загрузки класса по следующим причинам:

  1. Загрузчик классов не нашел класс в пути к классам (проверьте: проверьте, находятся ли загруженный класс и пакеты, от которых он зависит, в пути к классам)
  2. Класс был загружен в память JVM загрузчиком классов, и другой загрузчик классов пытается загрузить его из того же пакета.
Метод Object.getClass()

Если у нас есть объект класса, то мы можем получить объект Class этого класса с помощью метода Object.getClass.

// String 对象的 getClass 方法
Class clazz1 = "hello".getClass();
// 数组对象的 getClass 方法
Class clazz2 = (new byte[1024]).getClass();
System.out.println(class2) // 会输出 [B, [ 代表是数组, B 代表是 byte。即 byte 数组的类类型
синтаксис класса

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

// 类
Class clazz = Integer.class;
// 数组
Class clazz2 = int [][].class;
Статическое свойство TYPE класса-оболочки

Существуют соответствующие классы-оболочки для примитивных типов и типа void. В классе-оболочке есть статический атрибут TYPE, который сохраняет тип класса. Взяв в качестве примера класс Integer, в его исходном коде определены следующие статические свойства:

/**
 * The {@code Class} instance representing the primitive type
 * {@code int}.
 *
 * @since   JDK1.1
 */
@SuppressWarnings("unchecked")
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

Метод для создания экземпляра класса Class:

Class clazz1 = Integer.TYPE;
Class clazz2 = Void.TYPE;
Методы класса Class

Существуют методы получения других классов в классе, которые перечислены ниже:

  1. Class.getSuperclass(): получить суперкласс класса
  2. Class.getClasses() : получить массив Class, состоящий из всех общедоступных классов, интерфейсов и перечислений класса, включая унаследованные.
  3. Class.getDeclaredClasses(): получить массив Class, состоящий из всех классов, интерфейсов и перечислений, явно объявленных классом.
  4. (Класс/Поле/Метод/Конструктор).getDeclaringClass(): Получить класс, в котором находится класс/свойство/метод/конструктор.

Member & AccessibleObject

Прежде чем говорить о поле, методе и конструкторе, давайте поговорим о члене и доступном объекте. Member — это интерфейс, представляющий член Class, а первые три класса — это классы его реализации.

AccessibleObject — это общий родительский класс Field, Method и Constructor, который предоставляет возможность пометить отраженный объект как отменяющий проверку контроля доступа языка Java по умолчанию при его использовании. Уровень доступа может быть проигнорирован методом setAccessible для доступа к соответствующему содержимому. А AccessibleObject реализует интерфейс AnnotatedElement, предоставляя возможность получать аннотации.

Field

Поле предоставляет информацию об одном свойстве класса или интерфейса и возможность динамического доступа к нему.

Объекты полей можно получить с помощью методов, предоставляемых классом, следующим образом:

возвращаемое значение метода имя метода Описание метода
Field getDeclaredField(String name) Получить поле с указанным именем (включая приватную модификацию), исключая унаследованные поля
Field[] getDeclaredField() Получить все (включая измененные в частном порядке) поля класса или интерфейса, представленного объектом класса, за исключением унаследованных полей.
Field getField(String name) Получить поля с указанным именем и общедоступной модификацией, включая унаследованные поля
Field[] getField() Получить поля с модификатором public, включая унаследованные поля

Я не буду перечислять API, связанные с Field глобально, вы можете нажатьПосмотреть здесь.

Method

Метод предоставляет информацию об одном методе класса или интерфейса и возможность динамического доступа к нему.

Объекты полей можно получить с помощью методов, предоставляемых классом, следующим образом:

возвращаемое значение метода имя метода Описание метода
Method getDeclaredMethod(String name, Class<?>... parameterTypes) Возвращает объект Method с указанными параметрами, который отражает указанный объявленный метод класса или интерфейса, представленного этим объектом Class.
Method[] getDeclaredMethod() Возвращает массив объектов Method, которые отражают все методы, представляющие класс или интерфейс, объявленный этим объектом класса, включая открытые, защищенные, доступ по умолчанию (пакет) и частные методы, но не включая методы наследования.
Method getMethod(String name, Class<?>... parameterTypes) Возвращает объект Method, отражающий указанный открытый метод-член класса или интерфейса, представленного этим объектом класса.
Method[] getMethods() Возвращает массив, содержащий объекты Method, которые отражают общедоступные члены класса или интерфейса, представленного этим объектом класса (включая объявленные этим классом или интерфейсом и унаследованные от суперклассов и суперинтерфейсов).

API, связанный с методом, можно щелкнутьПосмотреть здесь.

Constructor

Constructor предоставляет информацию о конструкторе класса, а также возможность динамического доступа к нему.

Объекты конструктора можно получить с помощью методов, предоставляемых классом, следующим образом:

возвращаемое значение метода имя метода Описание метода
Constructor<T> getConstructor(Class<?>... parameterTypes) Возвращает объект конструктора указанного типа параметра с открытым доступом
Constructor<?>[] getConstructors() Возвращает массив объектов конструктора для всех конструкторов с публичным доступом
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Возвращает указанный тип параметра, все объявленные (включая приватные) объекты конструктора
Constructor<?>[] getDeclaredConstructor() Возвращает все объявленные (включая частные) объекты конструктора

Я не буду перечислять API, связанные с Constructor глобально, вы можете нажатьПосмотреть здесь.

Array

Array также является классом в Java. Array предоставляет статические методы для динамического создания массивов и доступа к элементам массива.

пройти черезgetXXX(Object array, int index)метод, передайте объект массива и индекс нижнего индекса, вы можете получить значение позиции.

пройти черезnewInstance(Class<?> componentType, int length)передайте тип и длину массива и создайте массив. следующее:

// 下面创建的两个数组等价
int x[] = new int[10];
int y[] = (int[]) Array.newInstance(int.class, 10);
// 输出 true
System.out.println(x.length == y.length);

пройти черезnewInstance(Class<?> componentType, int... dimensions)Метод создания многомерного массива. следующее:

// 下面创建的两个数组等价
int x[][] = new int[10][10];
int y[][] = (int[][]) Array.newInstance(int.class, 10, 10);
// 输出 true
System.out.println(x.length == y.length);

Сценарии применения рефлексии

Почти все Java-фреймворки используют отражение, такое как динамическая конфигурация: считывание значений записанного файла конфигурации, а затем установка этих значений в класс конфигурации через механизм отражения. В целом сценарии применения следующие:

  1. Используется при разработке фреймворка, например динамической конфигурации.
  2. Используется при разработке плагинов, таких как непрерывная интеграция
  3. Расширение приложения

Конечно, отражение также имеет некоторые недостатки:

  1. низкая производительность
  2. плохая читаемость
  3. Только ошибка во время работы