Рефлексия в Java — важная часть знаний, вы увидите отражение во многих местах. Он предоставляет языку Java возможность загружать, обнаруживать и использовать классы во время выполнения, которые совершенно неизвестны во время компиляции. Эта возможность очень распространена при написании фреймворков, таких как динамические прокси, сканирование классов и синтаксический анализ.
Определение и роль отражения
Механизм отражения: то есть язык Java имеет возможность самопросмотра во время выполнения, который может понять собственную ситуацию и подготовиться к следующему действию. Реакция заключается в том, что во время выполнения для класса мы можем знать, какие методы и свойства есть у класса. Для объекта мы можем вызвать любой из его методов. Это возможность динамически получать информацию о классе и динамически вызывать методы объекта.
Реализация основ отражения
Java предоставляет механизм отражения, основанный на классе Class и библиотеке классов java.lang.reflect. Его основные классы следующие:
- Класс: представляет класс или интерфейс.
- Поле: представляет переменную-член в классе
- Метод: представляет метод в классе.
- Конструктор: представляет конструктор класса.
- Массив: этот класс предоставляет статические методы для динамического создания массивов и доступа к элементам массива.
Class
Класс Класс — это соответствующий класс в Java, используемый для представления информации о типе среды выполнения. Фактически каждый класс в Java имеет объект класса, и каждый раз, когда мы пишем и компилируем вновь созданный класс, соответствующая информация записывается в файл .class. Когда мы создаем новый объект или обращаемся к статической переменной-члену, подсистема загрузчика классов в JVM загрузит соответствующий объект класса в JVM, а затем JVM создаст нужный нам объект экземпляра или предоставит статическую информацию на основе класса. объект, связанный с информацией этого типа.Ссылочное значение переменной. Мы можем вызвать класс Class как тип класса, а объект Class как объект типа класса (см. Thinking in Java).
Класс Class имеет следующие характеристики:
- Класс Класс также является типом класса, а класс — это ключевое слово.
- Класс Class имеет только один частный конструктор, и только JVM может создать экземпляр класса Class.
- Для объектов одного и того же класса в 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, которое возникает на этапе загрузки класса по следующим причинам:
- Загрузчик классов не нашел класс в пути к классам (проверьте: проверьте, находятся ли загруженный класс и пакеты, от которых он зависит, в пути к классам)
- Класс был загружен в память 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
Существуют методы получения других классов в классе, которые перечислены ниже:
- Class.getSuperclass(): получить суперкласс класса
- Class.getClasses() : получить массив Class, состоящий из всех общедоступных классов, интерфейсов и перечислений класса, включая унаследованные.
- Class.getDeclaredClasses(): получить массив Class, состоящий из всех классов, интерфейсов и перечислений, явно объявленных классом.
- (Класс/Поле/Метод/Конструктор).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-фреймворки используют отражение, такое как динамическая конфигурация: считывание значений записанного файла конфигурации, а затем установка этих значений в класс конфигурации через механизм отражения. В целом сценарии применения следующие:
- Используется при разработке фреймворка, например динамической конфигурации.
- Используется при разработке плагинов, таких как непрерывная интеграция
- Расширение приложения
Конечно, отражение также имеет некоторые недостатки:
- низкая производительность
- плохая читаемость
- Только ошибка во время работы