![translation](https://cdn.durumis.com/common/trans.png)
Это сообщение переведено AI.
Выбрать язык
Текст, резюмированный ИИ durumis
- Отражение — это API, который позволяет получать доступ к информации о классах во время выполнения и манипулировать ими по своему усмотрению.
- С помощью отражения можно создавать экземпляры классов и обращаться к полям и методам независимо от модификаторов доступа, что особенно полезно для динамического управления зависимостями на крупных этапах разработки, таких как фреймворки.
- Однако следует использовать его только в случае необходимости, поскольку он может нарушать инкапсуляцию и снижать производительность.
Что такое Reflection?
Reflection — это API, который позволяет создавать экземпляры нужного класса с помощью объекта типа Class, загруженного в кучу,и предоставляющий доступ к полям и методам этого экземпляра независимо от модификаторов доступа.
Загруженный класс — это класс, который был полностью загружен загрузчиком классов JVM из файла класса,и информация о нем была сохранена в объекте типа Class,который хранится в куче. Обратите внимание, что этот объект отличается от объекта, созданного с помощью ключевого слова new. Если вы не знакомы с объектами типа Class, ознакомьтесь с документацией JDK по java.lang.class.
Как использовать
Прежде чем использовать Reflection, необходимо получить объект типа Class, загруженный в кучу. Существует три способа сделать это.
- Получение объекта типа Class с помощью Class.class
- Получение объекта типа Class с помощью instance.getClass()
- Получение объекта типа Class с помощью Class.forName("имя класса")
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("Пароль — 1234.");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("Джейон", 23, "Разработка Dara");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{имя пакета}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Результат выполнения
1740000325
1740000325
Можно заметить, что все три метода возвращают один и тот же объект типа Class. Независимо от используемого метода хеш-код одинаков, поэтому можно выбирать любой из них в зависимости от ситуации.
Теперь, когда мы получили объект типа Class, мы можем создавать экземпляры этого класса, а также получать доступ к его полям и методам независимо от модификаторов доступа. Сначала создадим экземпляр класса.
public class Main {
public static void main(String[] args) throws Exception {
// Вывод всех конструкторов Member
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Создание экземпляра с помощью конструктора по умолчанию Member
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Создание экземпляра с помощью другого конструктора Member
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Джейон", 23, "Разработка Dara");
System.out.println("member3 = " + member3);
}
}
// Результат выполнения
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
С помощью getConstructor() можно получить конструктор и создать экземпляр Member с помощью newInstance().
Наконец, давайте получим доступ к полям и методам экземпляра, не обращая внимания на модификаторы доступа.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Джейон", 23, "Разработка Dara");
Class extends Member> memberClass = member.getClass();
// Доступ к полям
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "Джейон2");
System.out.println(member);
// Доступ к методам
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Тест Reflection");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Результат выполнения
Джейон
23
Разработка Dara
Member{name='Джейон2', age=23, hobby='Разработка Dara'}
Тест Reflection
С помощью getDeclaredFileds() можно получить все поля экземпляра класса, а с помощью get() — получить значение поля, а с помощью set() — изменить его. При этом обратите внимание, что при доступе к полям с модификатором private необходимо установить setAccessible() в true.
Методы можно получить с помощью getDeclaredMethod(). При этом необходимо передать в качестве аргументов имя метода и типы его параметров. Аналогично полям, при доступе к методам с модификатором private необходимо установить setAccessible() в true. Наконец, с помощью метода invoke() можно вызвать метод, полученный с помощью API Reflection.
Преимущества и недостатки
- Преимущества
- Reflection обеспечивает гибкость, позволяя создавать экземпляры классов во время выполнения, получать доступ к полям и методам независимо от модификаторов доступа, а также выполнять необходимые действия.
- Недостатки
- Нарушает инкапсуляцию.
- Создает экземпляры во время выполнения, поэтому типы нельзя проверить во время компиляции.
- Создает экземпляры во время выполнения, поэтому сложно отследить конкретный поток выполнения.
- Reflection работает медленнее, чем обычный доступ к полям и методам. (Не всегда медленнее.)
Причины использования
Reflection API позволяет получать доступ к информации о классах во время выполнения и манипулировать ими. Это касается даже полей и методов, объявленных с модификатором private. Может показаться, что Reflection — это опасная технология, нарушающая инкапсуляцию.
В небольших консольных приложениях разработчик может легко определить все используемые объекты и зависимости во время компиляции. Однако в крупных проектах, таких как фреймворки, отследить огромное количество объектов и зависимостей может быть сложно. Reflection позволяет создавать и устанавливать зависимости классов динамически.
Например, в Bean Factory в Spring, если к классу прикреплены аннотации @Controller, @Service или @Repository, Bean Factory автоматически создает и управляет этими классами. Разработчик не сообщал Bean Factory об этих классах. Это возможно благодаря Reflection. Во время выполнения поиск идет по классам, к которым прикреплены эти аннотации, и если они найдены, Reflection создает экземпляр класса, вставляет необходимые поля и сохраняет его в Bean Factory.
Конечно, как упоминалось выше, инкапсуляция нарушается, поэтому Reflection стоит использовать только в крайнем случае.