Рефлексия в Java: класс Class и метод getClass() - Вопросы
Всего: 5 вопросов
1. Что такое рефлексия (Reflection API) в Java и что возвращает метод getClass()?
Что такое рефлексия (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: 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() устарел и как правильно создать объект через рефлексию?
Почему метод 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-полю через рефлексию и почему для классов 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?
Что использовать в 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.