Это 15-й день моего участия в августовском испытании обновлений. Узнайте подробности события:Испытание августовского обновления
Смысл отражения:
Некоторые операции невозможно реализовать, если они жестко закодированы, и соответствующие параметры должны быть получены во время выполнения, прежде чем их можно будет использовать.
Что может сделать отражение
в соответствии с указаннымclass
и соответствующий экземплярobj
, получитьobj
по всем объектам звонитеobj
Все методы и конструкторы
типичная сцена
Версия весны в древние времена, 2.x, в то время основной была конфигурация xml, такая как следующий абзац, мы настроилиUser.class
Один примерuser
, структура Spring используетclass
Текстовое описание в атрибуте, начинающееся сClass.forName(className)
(или иначе, чтобы загрузитьUser
, а затем создайте конфигурацию на основе xmluser
пример
<bean id="user" class="com.wb.bean.User">
</bean>
рабочая сцена
Иногда нам нужно написать универсальный код, не запрограммировав должным образом указанный класс напрямую, или тип дженериков нельзя использовать для этой серии.
Например, мне нужно сгенерировать тестовый класс и назначить случайные назначения его полям.
В качестве альтернативы мне нужно скопировать экземпляр, если жестко запрограммировано что-то вродеb.setName(a.getName())
, он не будет универсальным (и код будет выглядеть скучно)
Или, у меня есть 2 набора шаблонов данных, которые нужно сопоставить из экземпляра класса А в экземпляр класса Б по определенным правилам (статья будет рассмотрена позже)
демонстрация кода
Продемонстрируйте наиболее распространенное использование рефлексии в нашей работе.
@ToString
// 本次演示用到的 bean
class User {
String name;
String job;
User() {}
private User(String name) {
this.name = name;
}
private void sayHi(String prev) {
System.out.printf("%s, I'm %s, %s is my job.", prev, name, job);
}
}
// 使用 junit 进行测试
@Test
public void f2() throws Exception {
// 获取到 class 的类型数据
Class<?> clz = User.class;
// Class<?> clz = Class.forName("com.wb.User"); // 如果我们的系统在运行之后从其他途径加载了 class 文件,我们可以使用根据文本描述的类路径来加载,比如,我们初学 jdbc 的时候就是这样加载 mysql 的 dirver
// 根据参数列表来选择对应的构造器
Constructor<?> oneParams = clz.getDeclaredConstructor(String.class);
// 设置可以获取到私有构造器
oneParams.setAccessible(true);
// 通过构造器进行实例化
Object obj = oneParams.newInstance("wangb");
System.out.println(obj); // 输出 User(name=wangb, job=null, dept=null)
// 根据文本描述去获取 job 这个字段
Field job = clz.getDeclaredField("job");
// 设置可以访问私有属性
job.setAccessible(true);
// 进行赋值操作
job.set(obj, "touch-fish");
System.out.println(obj); // 输出 User(name=wangb, job=touch-fish, dept=null)
// 获取 clz 上的 sayHi(String) 方法
Method sayHi = clz.getDeclaredMethod("sayHi", String.class);
// 通用设置为获取私有方法
sayHi.setAccessible(true);
// 进行方法调用
sayHi.invoke(obj, "morning"); // morning, I'm wangb, touch-fish is my job.
}
Через приведенный выше тестовый пример у нас будет очень сильное чувство
Мы стоим в перспективе бога, зная, какие свойства можно получить, какие методы и конструкторы использовать, а этих вещей, когда мы кодируем, не существует!
Подробности
Используемые нами классы, связанные с отражением, расположены вjava.lang.reflect
пройти черезclz.getxxx
При получении свойств, методов и конструкторов есть 2 варианта.В качестве примера возьмем получение свойств.
Field name = clz.getDeclaredField(name);
Field name2 = clz.getField(name);
Первый выполняет дополнительный шагname.setAccessible(true);
для доступа к закрытым полям, а затемname
Читать и писать; последнее можно получить толькоpublic
Данные
получить все конструкторы
Constructor<?>[] constructors = clz.getConstructors();
получить все методы
Method[] methods = clz.getDeclaredMethods();
получить все поля
Field[] fields = clz.getDeclaredFields();
Когда нам нужно получить все свойства, мы обычно используем цикл foreach для обхода
Как показано ниже:
for (Field f : fields) {
// do somethings
}
наконец
Хотя отражение является расширенной функцией, существует не так много API-интерфейсов. Важно то, что после того, как мы поймем, что может сделать отражение, мы обнаружим, что это несложно. Это не что иное, как обход дважды по мере необходимости для достижения того, чего мы хотим.
Конечно, следует отметить, что все-таки это не прямой звонок,Отражение будет иметь проблемы с производительностью. Если его нужно часто вызывать на основе отражения, лучше всего кешировать связанныеFields, Methods
, чтобы избежать потери производительности, вызванной повторным получением
Оригинал статьи, перепечатка без разрешения запрещена
-- от Easy Salted Fish