Отражение Java полностью проанализировано

Java

Эта статья была разрешена для публикации исключительно в общедоступной учетной записи WeChat guolin_blog (Го Линь).

Пожалуйста, укажите источник:woohoo.brief.com/afraid/607 метод 4 о 79…

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

Если вы найдете какие-либо ошибки, вы можете критиковать и исправлять их.

Большинство соответствующих точек знаний в этой статье резюмируются изОфициальная документация Oracle, для друзей, которые лучше говорят по-английски, рекомендуется читать исходный документ напрямую.

В качестве примера сначала опишем определение.

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

Короче говоря, вы можете сделать это через рефлекторный механизм в запущенном состоянии:

  • Для любого класса вы можете знать все свойства и методы этого класса;
  • Для любого объекта может быть вызван любой его метод и свойство;

Информация, полученная динамически, и функция динамического вызова метода объекта называется механизмом отражения языка java.

На мой взгляд, мы обычно используем Java-рефлексию, в основном задействующую два класса (интерфейса)Class,MemberЕсли два класса четкие, отражение в основном нормальное.

Class

Когда дело доходит до отражения, мы должны упомянуть Класс, который можно назвать основой для отражения; обратите внимание на упомянутые здесь ключевые слова Class и class.не то же самое. Ключевое слово class используется при объявлении класса java; в то время как Class — это класс, предоставляемый java JDK, полный путьjava.lang.Class, что по существу совпадает с Math, String или вашим собственным определением различных классов.

public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
   ...
}

Итак, какова роль класса в отражении?

For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information. Class also provides the ability to create new classes and objects. Most importantly, it is the entry point for all of the Reflection APIs.

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

Ниже приведен простой пример, иллюстрирующий создание экземпляра объекта посредством отражения.

public class Animal {
    private String name;
    private int age;
    public Animal(String name, int age){
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Animal : name = " + name + " age = " + age;
    }
}

 public class TestReflection{
    private static final String TAG = "Reflection";
    public void testReflection(){
        //获取Animal类的Class对象
        Class c = Animal.class;
        try {
            //通过Class对象反射获取Animal类的构造方法
            Constructor constructor = c.getConstructor(String.class, int.class);
            //调用构造方法获取Animal实例
            Animal animal = (Animal) constructor.newInstance( "Jack", 3);
            //将构造出来的Animal对象打印出来
            Log.d(TAG, animal.toString());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Теперь давайте посмотрим на значение печати

03-28 20:12:00.958 2835-2835/? D/Reflection: Animal : name = Jack age = 3

Видно, что мы действительно успешно сконструировали объект Animal, и Class внес большой вклад в этот процесс.

Некоторые люди говорят, что это слишком большая проблема для вас.Все знают объект Животное.Я могу дать вам новый в течение нескольких минут.

Animal animal = new Animal("Jack", 3);

Да!但是如果并不能直接导入Animal类呢,如果构造方法都是private的呢?这个时候反射就能大展身手了。

Как получить класс

Еще одна причина сказать, что класс является основой для отражения: пакет отражения Javajava.lang.reflectВсе классы в классе не имеют открытого конструктора, чтобы получить эти экземпляры класса, вы можете получить их только через класс Class. Поэтому, если вы хотите использовать отражение, вы должны получить объект класса.
Ниже перечислены несколько методов, которые могут получить объект класса.

  • Object.getClass()
    Получите соответствующий объект Class через экземпляр объекта, например
//Returns the Class for String
Class c = "foo".getClass();

enum E { A, B }
//Returns the Class corresponding to the enumeration type E.
Class c = A.getClass();

byte[] bytes = new byte[1024];
//Returns the Class corresponding to an array with component type byte.
Class c = bytes.getClass();

Set<String> s = new HashSet<String>();
//Returns the Class corresponding to java.util.HashSet.
Class c = s.getClass();

Однако этот метод нельзя использовать для примитивных типов.

boolean b;
Class c = b.getClass();   // compile-time error
  • The .class Syntax
    Объект класса получается по типу класса, а базовый тип также может использовать этот метод, такой как
//The `.class` syntax returns the Class corresponding to the type `boolean`.
Class c = boolean.class;  

//Returns the Class for String
Class c = String.class;
  • Class.forName()
    Получите объект класса по полностью квалифицированному названию класса, базовые типы не могут использовать этот метод
Class c = Class.forName("java.lang.String");

Специально для массивов

Class cDoubleArray = Class.forName("[D");    //相当于double[].class

Class cStringArray = Class.forName("[[Ljava.lang.String;");   //相当于String[][].class
  • TYPE Field for Primitive Type Wrappers
    Классы-оболочки примитивных и пустых типов можно получить с помощью поля TYPE.
Class c = Double.TYPE;   //等价于 double.class.

Class c = Void.TYPE;
  • Methods that Return Classes
    Существуют также некоторые методы отражения для получения объекта класса, но предпосылка заключается в том, что вы уже получили объект класса.
    Это немного сложно.Например, если вы получили объект класса класса, вы можете получить объект класса родительского класса этого класса с помощью метода отражения.

Class.getSuperclass()
Получить родительский класс Class данного класса

// javax.swing.JButton的父类是javax.swing.AbstractButton
Class c = javax.swing.JButton.class.getSuperclass();

Аналогичные методы:
Class.getClasses()
Class.getDeclaredClasses()
Class.getDeclaringClass()
Class.getEnclosingClass()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()

Получить модификатор класса и ввести по классу

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


class.png

Давайте возьмем HashMap в качестве примера и воспользуемся демонстрацией, чтобы проиллюстрировать, как получить эту информацию.

public class TestReflection {
    private static final String TAG = "Reflection";
    public void testReflection() {
        Class<?> c = HashMap.class;
        //获取类名
        Log.d(TAG, "Class : " + c.getCanonicalName());
        //获取类限定符
        Log.d(TAG, "Modifiers : " + Modifier.toString(c.getModifiers()));
        //获取类泛型信息
        TypeVariable[] tv = c.getTypeParameters();
        if (tv.length != 0) {
            StringBuilder parameter = new StringBuilder("Parameters : ");
            for (TypeVariable t : tv) {
                parameter.append(t.getName());
                parameter.append(" ");
            }
            Log.d(TAG, parameter.toString());
        } else {
            Log.d(TAG, "  -- No Type Parameters --");
        }
        //获取类实现的所有接口
        Type[] intfs = c.getGenericInterfaces();
        if (intfs.length != 0) {
            StringBuilder interfaces = new StringBuilder("Implemented Interfaces : ");
            for (Type intf : intfs){
                interfaces.append(intf.toString());
                interfaces.append(" ");
            }
            Log.d(TAG, interfaces.toString());
        } else {
            Log.d(TAG, "  -- No Implemented Interfaces --");
        }
        //获取类继承数上的所有父类
        List<Class> l = new ArrayList<>();
        printAncestor(c, l);
        if (l.size() != 0) {
            StringBuilder inheritance = new StringBuilder("Inheritance Path : ");
            for (Class<?> cl : l){
                inheritance.append(cl.getCanonicalName());
                inheritance.append(" ");
            }
            Log.d(TAG, inheritance.toString());
        } else {
            Log.d(TAG, "  -- No Super Classes --%n%n");
        }
        //获取类的注解(只能获取到 RUNTIME 类型的注解)
        Annotation[] ann = c.getAnnotations();
        if (ann.length != 0) {
            StringBuilder annotation = new StringBuilder("Annotations : ");
            for (Annotation a : ann){
                annotation.append(a.toString());
                annotation.append(" ");
            }
            Log.d(TAG, annotation.toString());
        } else {
            Log.d(TAG, "  -- No Annotations --%n%n");
        }
    }
    private static void printAncestor(Class<?> c, List<Class> l) {
        Class<?> ancestor = c.getSuperclass();
        if (ancestor != null) {
            l.add(ancestor);
            printAncestor(ancestor, l);
        }
    }
}

Результат печати следующий

03-29 15:04:23.070 27826-27826/com.example.ming.testproject D/Reflection: Class : java.util.HashMap
03-29 15:04:23.070 27826-27826/com.example.ming.testproject D/Reflection: Modifiers : public
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection: Parameters : K  V  
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection: Implemented Interfaces : java.util.Map<K, V>  interface java.lang.Cloneable  interface java.io.Serializable  
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection: Inheritance Path : java.util.AbstractMap  java.lang.Object  
03-29 15:04:23.071 27826-27826/com.example.ming.testproject D/Reflection:   -- No Annotations --

Member

Reflection defines an interface java.lang.reflect.Member which is implemented by java.lang.reflect.Field, java.lang.reflect.Method, and java.lang.reflect.Constructor .

Для интерфейса Member некоторые люди могут не знать, что он делает, но если вы упомянете три класса реализации, которые его реализуют, предполагается, что люди, которые использовали отражение, узнают об этом. Мы знаем, что члены класса в основном включают构造函数,变量и方法, операции в Java в основном связаны с этими тремя, и три класса реализации члена соответствуют им соответственно.

java.lang.reflect.Field: Соответствующая переменная класса
java.lang.reflect.Method: соответствующий метод класса
java.lang.reflect.Constructor: соответствует конструктору класса

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

Сначала создайте тестовый класс

public class Cat {
    public static final String TAG = Cat.class.getSimpleName();
    private String name;
    @Deprecated
    public int age;
    
    public Cat(String name, int age){
        this.name = name;
        this.age = age;
    }

    public String getName(){
        return name;
    }

    public void eat(String food){
        Log.d(TAG, "eat food " + food);
    }

    public void eat(String... foods){
        StringBuilder s = new StringBuilder();
        for(String food : foods){
            s.append(food);
            s.append(" ");
        }
        Log.d(TAG, "eat food " + s.toString());
    }

    public void sleep(){
        Log.d(TAG, "sleep");
    }

    @Override
    public String toString() {
        return "name = " + name + " age = " + age;
    }
}
Field

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

  • GetField
    Класс предоставляет 4 метода для получения поля данного класса.
    getDeclaredField(String name)
    Получить указанную переменную (если это объявленная переменная, в том числе частная)
    getField(String name)
    Получить указанную переменную (только публичные)
    getDeclaredFields()
    Получить все объявленные переменные (включая частные)
    getFields()
    Получить все общедоступные переменные

  • Получить типы переменных, модификаторы, аннотации
    Пример, иллюстрирующий проблему

    public void testField(){
        Class c = Cat.class;
        Field[] fields = c.getDeclaredFields();
        for(Field f : fields){
            StringBuilder builder = new StringBuilder();
            //获取名称
            builder.append("filed name = ");
            builder.append(f.getName());
            //获取类型
            builder.append(" type = ");
            builder.append(f.getType());
            //获取修饰符
            builder.append(" modifiers = ");
            builder.append(Modifier.toString(f.getModifiers()));
            //获取注解
            Annotation[] ann = f.getAnnotations();
            if (ann.length != 0) {
                builder.append(" annotations = ");
                for (Annotation a : ann){
                    builder.append(a.toString());
                    builder.append(" ");
                }
            } else {
                builder.append("  -- No Annotations --");
            }
            Log.d(TAG, builder.toString());
        }
    }

распечатать результат:

filed name = age type = int modifiers = public annotations = @java.lang.Deprecated() 
filed name = name type = class java.lang.String modifiers = private  -- No Annotations --
filed name = TAG type = class java.lang.String modifiers = public static final  -- No Annotations --
  • Получить и установить значение переменной
    Учитывая объект и имя его переменной-члена, значение этой переменной может быть получено и изменено посредством отражения.
    Ничего не скажешь, ничего невозможно решить на примере, Easy~
    Вышеупомянутый тестовый класс по-прежнему получает и изменяет имя и возрастную кошку путем отражения.
    public void testField(){
        Cat cat = new Cat("Tom", 2);
        Class c = cat.getClass();
        try {
            //注意获取private变量时,需要用getDeclaredField
            Field fieldName = c.getDeclaredField("name");
            Field fieldAge = c.getField("age");
            //反射获取名字, 年龄
            String name = (String) fieldName.get(cat);
            int age = fieldAge.getInt(cat);
            Log.d(TAG, "before set, Cat name = " + name + " age = " + age);
            //反射重新set名字和年龄
            fieldName.set(cat, "Timmy");
            fieldAge.setInt(cat, 3);
            Log.d(TAG, "after set, Cat " + cat.toString());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

Хорошо? Сообщили об ошибке?

System.err: java.lang.IllegalAccessException: Class java.lang.Class<com.example.ming.testnestscrollview.TestReflection> cannot access private  field java.lang.String com.example.ming.testnestscrollview.Cat.name of class java.lang.Class<com.example.ming.testnestscrollview.Cat>
System.err:     at java.lang.reflect.Field.get(Native Method)
System.err:     at com.example.ming.testnestscrollview.TestReflection.testField(TestReflection.java:22)
System.err:     at com.example.ming.testnestscrollview.MainActivity.onCreate(MainActivity.java:17)

Соблюдайте информацию об исключенииjava.lang.IllegalAccessException, говоря, что у нас нет разрешения на манипулирование именем переменной; вернитесь к классу Cat и посмотрите на переменную имени.

    private String name;

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

  • java.lang.reflect.AccessibleObject
    AccessibleObjectпредоставляет нам методsetAccessible(boolean flag), функция этого метода состоит в том, чтобы отменить проверку прав доступа к языку Java. поэтому любое наследствоAccessibleObjectОбъекты класса могут быть отменены с помощью этого метода Проверка доступа к языку Java. (таким же образом можно получить доступ к переменным конечного типа)
public final class Field extends AccessibleObject implements Member

FieldточноAccessibleObject, это так же просто, как вызов перед доступом к закрытым переменнымfiled.setAccessible(true)просто хорошо

            ...
            fieldName.setAccessible(true);
            //反射获取名字, 年龄
            String name = (String) fieldName.get(cat);
            ...

распечатать результат

TestReflection: before set, Cat name = Tom age = 2
TestReflection: after set, Cat name = Timmy age = 3

Bingo!

УведомлениеMethodиConstructorтакже унаследовалAccessibleObject, поэтому, если вы столкнетесь с закрытыми методами и закрытыми конструкторами, к которым нет доступа, не забудьте обращаться с ними таким же образом.

Method

The java.lang.reflect.Method class provides APIs to access information about a method's modifiers, return type, parameters, annotations, and thrown exceptions. It also be used to invoke methods.

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

  • получить метод
    Класс по-прежнему предоставляет 4 способа получения метода:
    getDeclaredMethod(String name, Class<?>... parameterTypes)
    Получите указанный метод в соответствии с именем метода, имя параметра — это имя метода, а параметр parameterTypes — это тип параметра метода, например, getDeclaredMethod("eat", String.class)
    getMethod(String name, Class<?>... parameterTypes)
    Получить указанный общедоступный метод в соответствии с именем метода, остальные такие же, как указано выше.
    getDeclaredMethods()
    метод для получения всех объявлений
    getMethods()
    Получить все общедоступные методы

Примечание. При получении метода с параметрами, если тип параметра неправильный, он сообщитNoSuchMethodException, в случае, когда параметр является универсальным типом, универсальный тип должен рассматриваться как объект (Object.class).

  • получить тип возвращаемого значения метода
    getReturnType()Получить объект класса, соответствующий возвращаемому типу целевого метода.
    getGenericReturnType()Получить объект Type, соответствующий возвращаемому типу целевого метода.
    В чем разница между этими двумя методами?
    1. Тип возврата getReturnType() — Class, а тип возврата getGenericReturnType() — Type; Class реализует Type.
    2. Возвращаемое значение — обычные простые типы, такие как Object, int, String и т. д. Возвращаемое значение getGenericReturnType() такое же, как и у getReturnType().
      Напримерpublic String function1()
      Тогда соответствующие возвращаемые значения:
      getReturnType() : class java.lang.String
      getGenericReturnType() : class java.lang.String
    3. Возвращаемое значение является общим
      Напримерpublic T function2()
      Тогда соответствующие возвращаемые значения:
      getReturnType() : class java.lang.Object
      getGenericReturnType() : T
    4. Возвращаемое значение - тип параметризации
      Напримерpublic Class<String> function3()
      Тогда соответствующие возвращаемые значения:
      getReturnType() : class java.lang.Class
      getGenericReturnType() : java.lang.Class<java.lang.String>

В самом деле, все формы в отраженииgetGenericXXX()Правила метода аналогичны описанным выше.

  • Получить тип параметра метода
    getParameterTypes()Получите объект класса, соответствующий каждому типу параметра целевого метода.
    getGenericParameterTypes()Получите объект Type, соответствующий каждому типу параметра целевого метода.
    Возвращаемое значение представляет собой массив, и разница между ними такая же, как и в приведенном выше «Различии типа возвращаемого значения метода».

  • Получить тип исключения, вызванного объявлением метода
    getExceptionTypes()Получить объект класса, соответствующий типу исключения, созданному целевым методом.
    getGenericExceptionTypes()Получить объект Type, соответствующий типу исключения, созданному целевым методом.
    Возвращаемое значение представляет собой массив, разница такая же, как и выше.

  • Получить имя параметра метода
    Имя параметра метода по умолчанию не хранится в файле .class.Если вы хотите получить имя параметра метода, вам нужно добавить его при компиляции-parametersпараметр. (Метод получения параметров для конструктора)

//这里的m可以是普通方法Method,也可以是构造方法Constructor
//获取方法所有参数
Parameter[] params = m.getParameters();
for (int i = 0; i < params.length; i++) {
    Parameter p = params[i];
    p.getType();   //获取参数类型
    p.getName();  //获取参数名称,如果编译时未加上`-parameters`,返回的名称形如`argX`, X为参数在方法声明中的位置,从0开始
    p.getModifiers(); //获取参数修饰符
    p.isNamePresent();  //.class文件中是否保存参数名称, 编译时加上`-parameters`返回true,反之flase
}

Подробнее о получении имен параметров метода см. в официальном примере Oracle.MethodParameterSpy

  • получить модификатор метода
    Метод аналогичен методу Филда и т. д.
method.getModifiers();

Ps: Кстати, представьте еще несколько методов

  1. method.isVarArgs()// Определяем, является ли параметр метода переменным параметром
public Constructor<T> getConstructor(Class<?>... parameterTypes)  //返回true
public Constructor<T> getConstructor(Class<?> [] parameterTypes)  //返回flase
  1. method.isSynthetic()//Чтобы определить, является ли это составным методом, я лично понимаю, что составной метод - это метод, сгенерированный компилятором во время компиляции, а не метод в исходном коде
  2. method.isBridge()//Определяем, является ли этот метод мостом.Метод моста — это метод, автоматически сгенерированный компилятором, чтобы сделать байт-код, сгенерированный универсальным методом Java, совместимым с байт-кодом до версии 1.5 после введения универсальных методов в JDK 1.5. может относиться кwoo woo woo.cn blog on.com/this is a 88/afraid/758…
  • Вызов метода через отражение
    отражение через методinvoke()метод для вызова целевого метода. Первый параметр — вызываемый объект целевого класса.Если метод статический, параметр имеет значение null. Следующие параметры являются значениями параметров целевого метода в том же порядке, что и параметры в объявлении целевого метода.
public native Object invoke(Object obj, Object... args)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

Или возьмите приведенный выше тестовый класс Cat в качестве примера.

Примечание. Если метод является закрытым, вы можете использоватьmethod.setAccessible(true)Способы обойти проверки разрешений

 Class<?> c = Cat.class;
 try {
     //构造Cat实例
     Constructor constructor = c.getConstructor(String.class, int.class);
     Object cat = constructor.newInstance( "Jack", 3);
     //调用无参方法
     Method sleep = c.getDeclaredMethod("sleep");
     sleep.invoke(cat);
     //调用定项参数方法
     Method eat = c.getDeclaredMethod("eat", String.class);
     eat.invoke(cat, "grass");
     //调用不定项参数方法
     //不定项参数可以当成数组来处理
     Class[] argTypes = new Class[] { String[].class };
     Method varargsEat = c.getDeclaredMethod("eat", argTypes);
     String[] foods = new String[]{
          "grass", "meat"
     };
     varargsEat.invoke(cat, (Object)foods);
  } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
     e.printStackTrace();
 }

Исключение, вызванное самим вызываемым методом, будет отражено в отражении.InvocationTargetExceptionбросать. Другими словами, если во время вызова отражения возникает исключениеInvocationTargetExceptionВыдает, указывая на то, что сам вызов отражения выполнен успешно, поскольку это исключение является исключением, созданным самим целевым методом.

Constructor

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

  • получить конструктор
    Подобно методу, класс также предоставляет конструктору 4 метода для получения
    getDeclaredConstructor(Class<?>... parameterTypes)
    Получить указанный конструктор, параметр parameterTypes — это тип параметра конструктора.
    getConstructor(Class<?>... parameterTypes)
    Получите указанный общедоступный конструктор, параметр parameterTypes — это тип параметра конструктора.
    getDeclaredConstructors()
    Получить все объявленные конструкторы
    getConstructors()
    Получить все общедоступные конструкторы

Имя, квалификаторы, параметры, объявленные исключения и другие методы получения конструктора аналогичны методам, см. Метод

  • создать объект
    Есть два способа создания объектов посредством отражения:
    java.lang.reflect.Constructor.newInstance()
    Class.newInstance()
    Вообще говоря, мы предпочитаем первый метод, так в чем сходство и различие между двумя методами?
  1. Class.newInstance()Может использоваться только для вызова конструкторов без параметров;Constructor.newInstance()Конструкторы можно вызывать с произвольными аргументами.
  2. Class.newInstance()Исключение, сгенерированное в конструкторе, будет сгенерировано без обработки;Constructor.newInstance()Исключения, созданные в конструкторе, будут упакованы какInvocationTargetExceptionбросать.
  3. Class.newInstance()Требует доступа к конструктору;Constructor.newInstance()в состоянии пройтиsetAccessible(true)метод обхода разрешения на доступ к частному конструктору.

Пример был написан в разделе «Метод», а здесь он непосредственно перехвачен.

Class<?> c = Cat.class;
try {
    Constructor constructor = c.getConstructor(String.class, int.class);
    Cat cat = (Cat) constructor.newInstance( "Jack", 3);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
    e.printStackTrace();
}

Примечание. Отражение не поддерживает автоматическую упаковку, будьте осторожны при передаче параметров (автоматическая упаковка выполняется во время компиляции, а отражение — во время выполнения).

Массивы и перечисления

Массивы и перечисления также являются объектами, но в отражении создание массивов и перечислений и доступ к ним немного отличаются от обычных объектов, поэтому отражение в Java предоставляет некоторые специфические API-интерфейсы для массивов и перечислений.

множество
  • тип массива
    Тип массива: Массив по сути является объектом, поэтому он также имеет свой собственный тип.
    Например дляint[] intArray, тип массиваclass [I. в типе массива[Число представляет размерность массива, например[представляет собой одномерный массив,[[Представляет двумерный массив;[Следующие буквы представляют тип элемента массива,Iпредставлятьint, обычно первая буква в типе заглавная (за исключением длинного типа, которым является J).
class [B    //byte类型一维数组
class [S    //short类型一维数组
class [I    //int类型一维数组
class [C    //char类型一维数组
class [J    //long类型一维数组,J代表long类型,因为L被引用对象类型占用了
class [F    //float类型一维数组
class [D    //double类型一维数组
class [Lcom.dada.Season    //引用类型一维数组
class [[Ljava.lang.String  //引用类型二维数组
//获取一个变量的类型
Class<?> c = field.getType();
//判断该变量是否为数组
if (c.isArray()) {
   //获取数组的元素类型
   c.getComponentType()
}
  • Создать и инициализировать массив
    Отражение Java дает намjava.lang.reflect.ArrayКлассы используются для создания и инициализации массивов.
//创建数组, 参数componentType为数组元素的类型,后面不定项参数的个数代表数组的维度,参数值为数组长度
Array.newInstance(Class<?> componentType, int... dimensions)

//设置数组值,array为数组对象,index为数组的下标,value为需要设置的值
Array.set(Object array, int index, int value)

//获取数组的值,array为数组对象,index为数组的下标
Array.get(Object array, int index)

Пример, созданный с отражениемint[] array = new int[]{1, 2}

Object array = Array.newInstance(int.class, 2);
Array.setInt(array , 0, 1);
Array.setInt(array , 1, 2);

Примечание. Отражение поддерживает автоматическое расширение данных, но не допускает сужения данных (сужение? Действительно сложно перевести). Это означает, что для вышеуказанного метода set вы можете установить данные короткого типа в массиве типа int, но вы не можете установить данные длинного типа, иначе будет сообщено об исключении IllegalArgumentException.

  • Многомерные массивы
    Отражение Java не предоставляет API для прямого доступа к элементам многомерных массивов, но вы можете рассматривать многомерные массивы как массивы массивов.
Object matrix = Array.newInstance(int.class, 2, 2);
Object row0 = Array.get(matrix, 0);
Object row1 = Array.get(matrix, 1);

Array.setInt(row0, 0, 1);
Array.setInt(row0, 1, 2);
Array.setInt(row1, 0, 3);
Array.setInt(row1, 1, 4);

или

Object matrix = Array.newInstance(int.class, 2);
Object row0 = Array.newInstance(int.class, 2);
Object row1 = Array.newInstance(int.class, 2);

Array.setInt(row0, 0, 1);
Array.setInt(row0, 1, 2);
Array.setInt(row1, 0, 3);
Array.setInt(row1, 1, 4);

Array.set(matrix, 0, row0);
Array.set(matrix, 1, row1);
перечислить

Перечисления неявно наследуются отjava.lang.Enum, Enum наследуется от Object, поэтому сущность перечисления также является классом, и он также может иметь переменные-члены, методы построения, методы и т. д.; для методов отражения, которые могут использовать обычные классы, может использоваться перечисление; кроме того, Отражение Java предоставляет несколько дополнительных методов для службы перечисления.
Class.isEnum()
Indicates whether this class represents an enum type
Class.getEnumConstants()
Retrieves the list of enum constants defined by the enum in the order they're declared
java.lang.reflect.Field.isEnumConstant()
Indicates whether this field represents an element of an enumerated type

Недостатки отражения

Ни одна технология не идеальна, и хотя отражение Java является мощным средством, оно также имеет некоторые побочные эффекты.

  • накладные расходы на производительность
    Отражение включает динамическое разрешение типов, поэтому JVM не может оптимизировать этот код. Следовательно, рефлексивные операции гораздо менее эффективны, чем нерефлексивные. Мы должны избегать использования отражения в часто выполняемом коде или в программах, критичных к производительности.
  • ограничения безопасности
    Использование технологии отражения требует, чтобы программа работала в среде без ограничений безопасности. Это проблема, если программа должна работать в среде с ограниченной безопасностью, такой как апплет.
  • Внутреннее воздействие
    Поскольку отражение позволяет коду выполнять операции, которые обычно не разрешены (например, доступ к закрытым свойствам и методам), использование отражения может привести к непреднамеренным побочным эффектам — в коде появляются функциональные ошибки, снижающие переносимость. Рефлексивный код разрушает абстракцию, поэтому при изменении платформы поведение кода может меняться вместе с ней.

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