Метод отражения set(..)
работает с FieldAccessor
с.
Для int
он получает UnsafeQualifiedIntegerFieldAccessorImpl
, суперкласс которого определяет свойство readOnly
как истинное, только если поле имеет значение и static
, и final
Итак, чтобы сначала ответить на незаданный вопрос - вот почему final
изменяется без исключения.
Все подклассы UnsafeQualifiedFieldAccessor
используют класс sun.misc.Unsafe
для получения значений. Есть все методы native
, но их имена getVolatileInt(..)
и getInt(..)
(getVolatileObject(..)
и getObject(..)
соответственно). Вышеупомянутые средства доступа используют «изменчивую» версию. Вот что произойдет, если мы добавим энергонезависимую версию:
System.out.println("reflection: non-volatile primitiveInt = "
unsafe.getInt(test, (long) unsafe.fieldOffset(getField("primitiveInt"))));
(где unsafe
создается отражением - иначе не допускается)
(и я звоню getObject
для Integer
и String
)
Это дает интересные результаты:
reflection: primitiveInt = 84
direct: primitiveInt = 42
reflection: non-volatile primitiveInt = 84
reflection: wrappedInt = 84
direct: wrappedInt = 84
reflection: non-volatile wrappedInt = 84
reflection: stringValue = 84
direct: stringValue = 42
reflection: non-volatile stringValue = 84
В этот момент я вспоминаю статью на javaspecialists.eu , в которой обсуждается связанный с этим вопрос. Он цитирует JSR-133 :
Если конечное поле инициализируется константой времени компиляции в объявлении поля, изменения в последнем поле могут не наблюдаться, поскольку использование этого финального поля заменяется во время компиляции константой времени компиляции.
Глава 9 обсуждает детали, наблюдаемые в этом вопросе.
И оказывается, что это поведение не так уж неожиданно, поскольку изменение полей final
должно происходить только сразу после инициализации объекта.