Автор оригинала: Pankaj Kumar.
Отражение Java предоставляет возможность проверять и изменять поведение приложения во время выполнения. Отражение в Java является одной из передовых тем основной java. Используя отражение java, мы можем проверить класс, интерфейс , перечисление , получить информацию об их структуре, методах и полях во время выполнения, даже если класс недоступен во время компиляции. Мы также можем использовать отражение для создания экземпляра объекта, вызова его методов, изменения значений полей.
Отражение Java
- Отражение в Java
- Отражение Java для классов
- Получить Объект Класса
- Получите Супер Класс
- Получите Общедоступные Классы Участников
- Получить Объявленные Классы
- Получить Объявление Класса
- Получение Имени Пакета
- Получение Модификаторов классов
- Получить Параметры типа
- Получить Реализованные Интерфейсы
- Получить Все Общедоступные Методы
- Соберите Всех Публичных Конструкторов
- Получить Все Общедоступные Поля
- Получить Все Аннотации
- Отражение Java для полей
- Получить Публичное Поле
- Поле, Объявляющее Класс
- Получить Тип поля
- Получить/Установить Значение Общедоступного Поля
- Получить/Установить Значение Закрытого Поля
- Отражение Java для методов
- Получить Общедоступный Метод
- Вызов Общедоступного Метода
- Вызов Частных Методов
- Отражение Java для конструкторов
- Получить Общедоступный Конструктор
- Создание экземпляра объекта с помощью конструктора
- Отражение Java для аннотаций
Отражение в Java
Отражение в Java-очень мощная концепция, и она мало полезна в обычном программировании, но она является основой для большинства фреймворков Java, J2EE. Некоторые из фреймворков, использующих отражение java, являются:
- JUnit – использует отражение для анализа аннотации @Test, чтобы получить методы тестирования, а затем вызвать его.
- Spring – инъекция зависимостей, подробнее читайте на Инъекция зависимостей Spring
- Tomcat веб-контейнер для пересылки запроса на исправление модуля путем анализа их web.xml файлы и запросить URI.
- Eclipse автоматическое заполнение имен методов
- Распорки
- Зимовать
Список бесконечен, и все они используют отражение java, потому что все эти фреймворки не имеют знаний и доступа к определяемым пользователем классам, интерфейсам, их методам и т.д.
Мы не должны использовать отражение в обычном программировании, где у нас уже есть доступ к классам и интерфейсам, из-за следующих недостатков.
- Низкая производительность – Поскольку отражение java разрешает типы динамически, оно включает в себя обработку, такую как сканирование пути к классу, чтобы найти загружаемый класс, что приводит к снижению производительности.
- Ограничения безопасности – Для отражения требуются разрешения во время выполнения, которые могут быть недоступны для системы, работающей под управлением security manager. Это может привести к сбою приложения во время выполнения из-за диспетчера безопасности.
- Проблемы безопасности – Используя отражение, мы можем получить доступ к части кода, доступ к которой нам не разрешен, например, мы можем получить доступ к закрытым полям класса и изменить его значение. Это может представлять серьезную угрозу безопасности и привести к неправильному поведению вашего приложения.
- Высокий уровень обслуживания – Код отражения трудно понять и отладить, также любые проблемы с кодом не могут быть обнаружены во время компиляции, поскольку классы могут быть недоступны, что делает его менее гибким и сложным в обслуживании.
Отражение Java для классов
В java каждый объект является либо примитивным типом, либо ссылкой. Все классы, перечисления, массивы являются ссылочными типами и наследуются от java.lang.Объект . Примитивные типы – логический, байтовый, короткий, int, длинный, символ, плавающий и двойной.
ява.ланг.Класс является точкой входа для всех операций отражения. Для каждого типа объекта JVM создает неизменяемый экземпляр java.lang.Класс , предоставляющий методы для изучения свойств объекта во время выполнения и создания новых объектов, вызова его метода и получения/установки полей объекта.
В этом разделе мы рассмотрим важные методы класса, для удобства я создаю некоторые классы и интерфейсы с иерархией наследования.
package com.journaldev.reflection;
public interface BaseInterface {
public int interfaceInt=0;
void method1();
int method2(String str);
}
package com.journaldev.reflection;
public class BaseClass {
public int baseInt;
private static void method3(){
System.out.println("Method3");
}
public int method4(){
System.out.println("Method4");
return 0;
}
public static int method5(){
System.out.println("Method5");
return 0;
}
void method6(){
System.out.println("Method6");
}
// inner public class
public class BaseClassInnerClass{}
//member public enum
public enum BaseClassMemberEnum{}
}
package com.journaldev.reflection;
@Deprecated
public class ConcreteClass extends BaseClass implements BaseInterface {
public int publicInt;
private String privateString="private string";
protected boolean protectedBoolean;
Object defaultObject;
public ConcreteClass(int i){
this.publicInt=i;
}
@Override
public void method1() {
System.out.println("Method1 impl.");
}
@Override
public int method2(String str) {
System.out.println("Method2 impl.");
return 0;
}
@Override
public int method4(){
System.out.println("Method4 overriden.");
return 0;
}
public int method5(int i){
System.out.println("Method4 overriden.");
return 0;
}
// inner classes
public class ConcreteClassPublicClass{}
private class ConcreteClassPrivateClass{}
protected class ConcreteClassProtectedClass{}
class ConcreteClassDefaultClass{}
//member enum
enum ConcreteClassDefaultEnum{}
public enum ConcreteClassPublicEnum{}
//member interface
public interface ConcreteClassPublicInterface{}
}
Давайте рассмотрим некоторые из важных методов отражения для классов.
Получить Объект Класса
Мы можем получить класс объекта , используя три метода – через статическую переменную class , используя getClass() метод объекта и java.lang.Class.forName(Строка fullyClassifiedClassName) . Для примитивных типов и массивов мы можем использовать статическую переменную class . Классы-оболочки предоставляют другую статическую переменную ТИПА для получения класса.
// Get Class using reflection
Class> concreteClass = ConcreteClass.class;
concreteClass = new ConcreteClass(5).getClass();
try {
// below method is used most of the times in frameworks like JUnit
//Spring dependency injection, Tomcat web container
//Eclipse auto completion of method names, hibernate, Struts2 etc.
//because ConcreteClass is not available at compile time
concreteClass = Class.forName("com.journaldev.reflection.ConcreteClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(concreteClass.getCanonicalName()); // prints com.journaldev.reflection.ConcreteClass
//for primitive types, wrapper classes and arrays
Class> booleanClass = boolean.class;
System.out.println(booleanClass.getCanonicalName()); // prints boolean
Class> cDouble = Double.TYPE;
System.out.println(cDouble.getCanonicalName()); // prints double
Class> cDoubleArray = Class.forName("[D");
System.out.println(cDoubleArray.getCanonicalName()); //prints double[]
Class> twoDStringArray = String[][].class;
System.out.println(twoDStringArray.getCanonicalName()); // prints java.lang.String[][]
getCanonicalName() возвращает каноническое имя базового класса. Обратите внимание, что java.lang.Класс использует универсальные методы, это помогает фреймворкам убедиться, что полученный класс является подклассом базового класса фреймворка. Ознакомьтесь с Учебником по обобщениям Java , чтобы узнать о обобщениях и их подстановочных знаках.
Получите Супер Класс
Метод getSuperclass() для объекта класса возвращает суперкласс класса. Если этот класс представляет либо класс объектов, интерфейс, примитивный тип, либо void, то возвращается значение null. Если этот объект представляет класс массива, то возвращается объект класса, представляющий класс объекта.
Class> superClass = Class.forName("com.journaldev.reflection.ConcreteClass").getSuperclass();
System.out.println(superClass); // prints "class com.journaldev.reflection.BaseClass"
System.out.println(Object.class.getSuperclass()); // prints "null"
System.out.println(String[][].class.getSuperclass());// prints "class java.lang.Object"
Получите Общедоступные Классы Участников
Метод getClasses() представления класса объекта возвращает массив, содержащий объекты класса, представляющие все общедоступные классы, интерфейсы и перечисления, которые являются членами класса, представленного этим объектом класса. Сюда входят члены открытого класса и интерфейса, унаследованные от суперклассов, и члены открытого класса и интерфейса, объявленные классом. Этот метод возвращает массив длиной 0, если этот объект класса не имеет открытых классов-членов или интерфейсов или если этот объект класса представляет примитивный тип, класс массива или пустоту.
Class>[] classes = concreteClass.getClasses(); //[class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass, //class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum, //interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface, //class com.journaldev.reflection.BaseClass$BaseClassInnerClass, //class com.journaldev.reflection.BaseClass$BaseClassMemberEnum] System.out.println(Arrays.toString(classes));
Получить Объявленные Классы
Метод getDeclaredClasses() возвращает массив объектов класса, отражающих все классы и интерфейсы, объявленные как члены класса, представленного этим объектом класса. Возвращаемый массив не включает классы, объявленные в унаследованных классах и интерфейсах.
//getting all of the classes, interfaces, and enums that are explicitly declared in ConcreteClass
Class>[] explicitClasses = Class.forName("com.journaldev.reflection.ConcreteClass").getDeclaredClasses();
//prints [class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultEnum,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPrivateClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassProtectedClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum,
//interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface]
System.out.println(Arrays.toString(explicitClasses));
Получить Объявление Класса
Метод getDeclaringClass() возвращает объект класса, представляющий класс, в котором он был объявлен.
Class> innerClass = Class.forName("com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass");
//prints com.journaldev.reflection.ConcreteClass
System.out.println(innerClass.getDeclaringClass().getCanonicalName());
System.out.println(innerClass.getEnclosingClass().getCanonicalName());
Получение Имени Пакета
Метод getPackage() возвращает пакет для этого класса. Загрузчик классов этого класса используется для поиска пакета. Мы можем вызвать getName() метод пакета, чтобы получить имя пакета.
//prints "com.journaldev.reflection"
System.out.println(Class.forName("com.journaldev.reflection.BaseInterface").getPackage().getName());
Получение Модификаторов классов
Метод getModifiers() возвращает представление int модификаторов класса, мы можем использовать java.lang.reflect.Модификатор.toString() метод, позволяющий получить его в строковом формате, используемом в исходном коде.
System.out.println(Modifier.toString(concreteClass.getModifiers())); //prints "public"
//prints "public abstract interface"
System.out.println(Modifier.toString(Class.forName("com.journaldev.reflection.BaseInterface").getModifiers()));
Получить Параметры типа
Функция getTypeParameters() возвращает массив переменной типа, если с классом связаны какие-либо параметры типа. Параметры типа возвращаются в том же порядке, что и объявленные.
//Get Type parameters (generics)
TypeVariable>[] typeParameters = Class.forName("java.util.HashMap").getTypeParameters();
for(TypeVariable> t : typeParameters)
System.out.print(t.getName()+",");
Получить Реализованные Интерфейсы
Метод getGenericInterfaces() возвращает массив интерфейсов, реализованных классом, с информацией об универсальном типе. Мы также можем использовать getInterfaces() для получения представления классов всех реализованных интерфейсов.
Type[] interfaces = Class.forName("java.util.HashMap").getGenericInterfaces();
//prints "[java.util.Map, interface java.lang.Cloneable, interface java.io.Serializable]"
System.out.println(Arrays.toString(interfaces));
//prints "[interface java.util.Map, interface java.lang.Cloneable, interface java.io.Serializable]"
System.out.println(Arrays.toString(Class.forName("java.util.HashMap").getInterfaces()));
Получить Все Общедоступные Методы
Метод GetMethods() возвращает массив общедоступных методов Класса, включая общедоступные методы его суперклассов и суперинтерфейсов.
Method[] publicMethods = Class.forName("com.journaldev.reflection.ConcreteClass").getMethods();
//prints public methods of ConcreteClass, BaseClass, Object
System.out.println(Arrays.toString(publicMethods));
Соберите Всех Публичных Конструкторов
Метод getConstructors() возвращает список общедоступных конструкторов ссылки на класс объекта.
//Get All public constructors
Constructor>[] publicConstructors = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructors();
//prints public constructors of ConcreteClass
System.out.println(Arrays.toString(publicConstructors));
Получить Все Общедоступные Поля
Метод GetFields() возвращает массив открытых полей класса, включая открытые поля его суперклассов и суперинтерфейсов.
//Get All public fields
Field[] publicFields = Class.forName("com.journaldev.reflection.ConcreteClass").getFields();
//prints public fields of ConcreteClass, it's superclass and super interfaces
System.out.println(Arrays.toString(publicFields));
Получить Все Аннотации
Метод getAnnotations() возвращает все аннотации для элемента, мы также можем использовать его с классом, полями и методами. Обратите внимание, что только аннотации, доступные с отражением, соответствуют политике хранения среды выполнения, ознакомьтесь с Учебник по аннотациям Java . Мы рассмотрим это более подробно в последующих разделах.
java.lang.annotation.Annotation[] annotations = Class.forName("com.journaldev.reflection.ConcreteClass").getAnnotations();
//prints [@java.lang.Deprecated()]
System.out.println(Arrays.toString(annotations));
Отражение Java для полей
API отражения предоставляет несколько методов для анализа полей класса и изменения их значений во время выполнения. В этом разделе мы рассмотрим некоторые из часто используемых функций отражения для методов.
Получить Публичное Поле
В последнем разделе мы рассмотрели, как получить список всех открытых полей класса. API отражения также предоставляет метод для получения определенного открытого поля класса с помощью метода getField () . Этот метод ищет поле в указанной ссылке на класс, а затем в суперинтерфейсах, а затем в суперклассах.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("interfaceInt");
Приведенный выше вызов вернет поле из базового интерфейса, реализованного Конкретным классом. Если поле не найдено, оно вызывает исключение NoSuchFieldException.
Поле, Объявляющее Класс
Мы можем использовать getDeclaringClass() объекта поля, чтобы получить класс, объявляющий поле.
try {
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("interfaceInt");
Class> fieldClass = field.getDeclaringClass();
System.out.println(fieldClass.getCanonicalName()); //prints com.journaldev.reflection.BaseInterface
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
Получить Тип поля
Метод GetType() возвращает объект класса для объявленного типа поля, если поле является примитивным типом, он возвращает объект класса-оболочки.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("publicInt");
Class> fieldType = field.getType();
System.out.println(fieldType.getCanonicalName()); //prints int
Получить/Установить Значение Общедоступного Поля
Мы можем получить и установить значение поля в объекте с помощью отражения.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("publicInt");
ConcreteClass obj = new ConcreteClass(5);
System.out.println(field.get(obj)); //prints 5
field.setInt(obj, 10); //setting field value to 10 in object
System.out.println(field.get(obj)); //prints 10
метод get() возвращает объект, поэтому, если поле имеет примитивный тип, оно возвращает соответствующий класс-оболочку . Если поле статично, мы можем передать объект как null в методе get ().
Существует несколько методов set*() для установки объекта в поле или для установки различных типов примитивных типов в поле. Мы можем получить тип поля, а затем вызвать правильную функцию, чтобы правильно установить значение поля. Если поле является окончательным, методы set() выдают java.lang.Исключение незаконного доступа.
Получить/Установить Значение Закрытого Поля
Мы знаем, что частные поля и методы недоступны за пределами класса, но с помощью отражения мы можем получить/установить значение частного поля, отключив проверку доступа java на наличие модификаторов полей.
Field privateField = Class.forName("com.journaldev.reflection.ConcreteClass").getDeclaredField("privateString");
//turning off access check with below method call
privateField.setAccessible(true);
ConcreteClass objTest = new ConcreteClass(1);
System.out.println(privateField.get(objTest)); // prints "private string"
privateField.set(objTest, "private string updated");
System.out.println(privateField.get(objTest)); //prints "private string updated"
Отражение Java для методов
Используя отражение, мы можем получить информацию о методе, а также вызвать его. В этом разделе мы изучим различные способы получения метода, вызова метода и доступа к закрытым методам.
Получить Общедоступный Метод
Мы можем использовать GetMethod() для получения общедоступного метода класса, нам нужно передать имя метода и типы параметров метода. Если метод не найден в классе, API отражения ищет метод в суперклассе.
В приведенном ниже примере я получаю метод put() для хэш-карты с использованием отражения. В примере также показано, как получить типы параметров, модификаторы методов и тип возвращаемого значения метода.
Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class);
//get method parameter types, prints "[class java.lang.Object, class java.lang.Object]"
System.out.println(Arrays.toString(method.getParameterTypes()));
//get method return type, return "class java.lang.Object", class reference for void
System.out.println(method.getReturnType());
//get method modifiers
System.out.println(Modifier.toString(method.getModifiers())); //prints "public"
Вызов Общедоступного Метода
Мы можем использовать метод invoke() объекта метода для вызова метода, в приведенном ниже примере кода я вызываю метод put на хэш-карте с помощью отражения.
Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class);
Map hm = new HashMap<>();
method.invoke(hm, "key", "value");
System.out.println(hm); // prints {key=value}
Если метод статичен, мы можем передать значение NULL в качестве аргумента объекта.
Вызов Частных Методов
Мы можем использовать getDeclaredMethod (), чтобы получить закрытый метод, а затем отключить проверку доступа для его вызова. В приведенном ниже примере показано, как мы можем вызвать метод3() базового класса, который является статическим и не имеет параметров.
//invoking private method
Method method = Class.forName("com.journaldev.reflection.BaseClass").getDeclaredMethod("method3", null);
method.setAccessible(true);
method.invoke(null, null); //prints "Method3"
Отражение Java для конструкторов
API отражения предоставляет методы для анализа конструкторов класса, и мы можем создавать новые экземпляры класса, вызывая конструктор. Мы уже научились привлекать всех общественных строителей.
Получить Общедоступный Конструктор
Мы можем использовать метод getConstructor() для представления класса объекта, чтобы получить конкретный открытый конструктор. В приведенном ниже примере показано, как получить конструктор конкретного класса, определенного выше, и конструктор без аргументов HashMap. В нем также показано, как получить массив типов параметров для конструктора.
Constructor> constructor = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructor(int.class);
//getting constructor parameters
System.out.println(Arrays.toString(constructor.getParameterTypes())); // prints "[int]"
Constructor> hashMapConstructor = Class.forName("java.util.HashMap").getConstructor(null);
System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes())); // prints "[]"
Создание экземпляра объекта с помощью конструктора
Мы можем использовать метод newInstance() для объекта конструктора для создания нового экземпляра класса. Поскольку мы используем отражение, когда у нас нет информации о классах во время компиляции, мы можем назначить ее объекту, а затем использовать отражение для доступа к его полям и вызова его методов.
Constructor> constructor = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructor(int.class);
//getting constructor parameters
System.out.println(Arrays.toString(constructor.getParameterTypes())); // prints "[int]"
Object myObj = constructor.newInstance(10);
Method myObjMethod = myObj.getClass().getMethod("method1", null);
myObjMethod.invoke(myObj, null); //prints "Method1 impl."
Constructor> hashMapConstructor = Class.forName("java.util.HashMap").getConstructor(null);
System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes())); // prints "[]"
HashMap myMap = (HashMap) hashMapConstructor.newInstance(null);
Отражение для аннотаций
Аннотации были введены в Java 1.5 для предоставления метаданных о классе, методах или полях, и теперь они широко используются в таких фреймворках, как Spring и Hibernate. API отражения также был расширен для обеспечения поддержки анализа аннотаций во время выполнения.
Используя API отражения, мы можем анализировать аннотации, политика хранения которых является средой выполнения. Я уже написал подробное руководство по аннотациям и о том, как мы можем использовать API отражения для анализа аннотаций, поэтому я бы посоветовал вам ознакомиться с Учебником по аннотациям Java .
Это все для примера учебника по отражению Java, я надеюсь, вам понравился учебник и вы поняли важность API отражения Java.