Рефлексия в Java: класс Class и метод getClass() - Вопросы

Всего: 5 вопросов

1. 

Что такое рефлексия (Reflection API) в Java и что возвращает метод getClass()?

Рефлексия — механизм исследования программы во время выполнения: можно узнать, какие у класса поля, методы и конструкторы, создать объект по имени класса, вызвать метод и прочитать или изменить поле, даже приватное. Точка входа — класс java.lang.Class, а классы Field, Method, Constructor лежат в пакете java.lang.reflect. На рефлексии работают JUnit, Spring, Hibernate, Jackson.

getClass() — final-метод класса Object. Он возвращает объект Class, описывающий реальный тип объекта во время выполнения, а не тип переменной:

Object s = "строка";
System.out.println(s.getClass()); // class java.lang.String

Для каждого загруженного типа JVM создаёт ровно один объект Class, публичных конструкторов у Class нет. Вызов getClass() на null бросает NullPointerException.

2. 

Чем отличаются способы получить объект Class: getClass(), литерал .class и Class.forName()?

Все три способа возвращают один и тот же объект Class, различаются они тем, что нужно на входе:

  • obj.getClass() — есть объект, нужен его реальный тип. На null бросает NullPointerException, для примитивной переменной не компилируется.
  • Имя.class — тип известен при компиляции; работает для примитивов и массивов (int.class, int[].class). Класс не инициализирует: static-блоки не выполняются.
  • Class.forName("pkg.Имя") — имя известно только во время выполнения (конфигурация, плагин, JDBC-драйвер). Инициализирует класс, бросает checked-исключение ClassNotFoundException, в том числе для "int". Вложенный класс записывается через $: "java.util.Map$Entry", а не "java.util.Map.Entry". Без инициализации — перегрузка Class.forName(name, false, classLoader).
3. 

Почему метод Class.newInstance() устарел и как правильно создать объект через рефлексию?

С Java 9 Class.newInstance() помечен @Deprecated: он пробрасывает checked-исключения конструктора в обход компилятора — например, IOException из конструктора вылетает из метода, который его не объявляет.

Правильный способ — получить конструктор и вызвать его:

Class<?> c = Class.forName("HeavyBox");
Object box = c.getDeclaredConstructor().newInstance();

Тогда исключение конструктора приходит обёрнутым в InvocationTargetException (исходное доступно через getCause()). Для конструктора с параметрами типы передаются в getDeclaredConstructor(double.class), а значения — в newInstance(12.5).

4. 

Как получить доступ к private-полю через рефлексию и почему для классов JDK setAccessible(true) бросает InaccessibleObjectException?

Private-член находится через getDeclaredField() / getDeclaredMethod() (getField() и getMethod() видят только public), затем доступ открывается вызовом setAccessible(true):

Field label = c.getDeclaredField("label");
label.setAccessible(true);
label.set(box, "Хрупкое");

Для своих классов это работает без ограничений. Но с Java 16–17 пакеты модуля java.base закрыты для глубокой рефлексии, поэтому, например, String.class.getDeclaredField("value").setAccessible(true) бросает InaccessibleObjectException. Открыть такой пакет можно только явно — флагом JVM --add-opens.

5. 

Что использовать в equals(): getClass() или instanceof?

Оба варианта допустимы, но дают разную семантику, потому что getClass() сравнивает тип точно, а instanceof учитывает наследников:

Box box = new HeavyBox();
System.out.println(box instanceof Box);          // true
System.out.println(box.getClass() == Box.class); // false: реальный тип - HeavyBox

getClass() != o.getClass() считает объект наследника неравным объекту родителя и сохраняет симметричность equals() при наследовании. instanceof разрешает сравнение с подклассами — это удобно, если подклассы не добавляют полей, участвующих в сравнении, или класс объявлен final.

Страница 1 из 1