Ответ 1
Несмотря на то, что вы затеняете переменную, довольно интересно знать, что вы можете изменять финальные поля в java, поскольку вы можете читать здесь
Java 5 - "final" больше не является окончательным
Narve Saetre из Machina Networks в Норвегии вчера прислал мне записку, отметив, что было очень жаль, что мы могли бы изменить ручку на конечный массив. Я его неправильно понял и начал терпеливо объяснять что мы не могли бы сделать массив постоянным, и что не было никакого способа защищая содержимое массива. "Нет", сказал он, "мы можем изменить конечная рукоятка с использованием отражения."
Я попробовал Нарвать образец кода, и, как ни странно, Java 5 позволила мне изменить конечный дескриптор, даже ручку в примитивное поле! я знал это в какой-то момент это разрешалось, но затем оно было запрещено, поэтому я провел несколько тестов со старыми версиями Java. Во-первых, нам нужно класс с конечными полями:
public class Person { private final String name; private final int age; private final int iq = 110; private final Object country = "South Africa"; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return name + ", " + age + " of IQ=" + iq + " from " + country; } }
JDK 1.1.x
В JDK 1.1.x мы не смогли получить доступ к закрытым полям, используя отражение. Однако мы могли бы создать другого Лица с общественностью поля, затем скомпилируйте наш класс против этого и замените Person классы. Во время выполнения не было проверки доступа, если мы работали против другого класса к тому, с которым мы собрались. Тем не менее, мы не смогли восстановить окончательные поля во время выполнения, используя либо класс обмена или отражения.
JDK 1.1.8 JavaDocs для java.lang.reflect.Field имеет следующее сказать:
- Если этот объект Field принудительно использует контроль доступа к языку Java, а базовое поле недоступно, метод выдает IllegalAccessException.
- Если базовое поле является окончательным, метод генерирует исключение IllegalAccessException.
JDK 1.2.x
В JDK 1.2.x это немного изменилось. Теперь мы можем создавать частные поля доступный с помощью метода setAccessible (true). Доступ к полям теперь проверяется во время выполнения, поэтому мы не можем использовать трюк переключения классов для доступа к закрытым полям. Тем не менее, мы могли бы теперь внезапно отменить окончательный поля! Посмотрите на этот код:
import java.lang.reflect.Field; public class FinalFieldChange { private static void change(Person p, String name, Object value) throws NoSuchFieldException, IllegalAccessException { Field firstNameField = Person.class.getDeclaredField(name); firstNameField.setAccessible(true); firstNameField.set(p, value); } public static void main(String[] args) throws Exception { Person heinz = new Person("Heinz Kabutz", 32); change(heinz, "name", "Ng Keng Yap"); change(heinz, "age", new Integer(27)); change(heinz, "iq", new Integer(150)); change(heinz, "country", "Malaysia"); System.out.println(heinz); } }
Когда я запустил это в JDK 1.2.2_014, я получил следующий результат:
Ng Keng Yap, 27 of IQ=110 from Malaysia Note, no exceptions, no complaints, and an incorrect IQ result. It seems that if we set a
окончательное поле примитива во время объявления, значение вставляется, если тип является примитивным или String.
JDK 1.3.x и 1.4.x
В JDK 1.3.x Sun немного увеличил доступ и не позволил нам изменяя конечное поле с отражением. Так было и с JDK 1.4.x. Если мы попытаемся запустить класс FinalFieldChange для повторной привязки окончательные поля во время выполнения с использованием отражения, мы получим:
java version "1.3.1_12": Исходный поток "main" IllegalAccessException: поле окончательное на java.lang.reflect.Field.set(собственный метод) в FinalFieldChange.change(FinalFieldChange.java:8) в FinalFieldChange.main(FinalFieldChange.java:12)
java version "1.4.2_05" Исходный поток "main" IllegalAccessException: поле окончательное в java.lang.reflect.Field.set(Field.java:519) в FinalFieldChange.change(FinalFieldChange.java:8) в FinalFieldChange.main(FinalFieldChange.java:12)
JDK 5.x
Теперь мы переходим к JDK 5.x. Класс FinalFieldChange имеет тот же результат как в JDK 1.2.x:
Ng Keng Yap, 27 of IQ=110 from Malaysia When Narve Saetre mailed me that he managed to change a final field in JDK 5 using
я надеялся, что ошибка попала в JDK. Однако, мы оба чувствовали, что это маловероятно, особенно такая фундаментальная ошибка. После некоторого поиска я нашел модель памяти JSR-133: Java и Спецификация резьбы. Большая часть спецификации - это трудное чтение, и напоминает мне о моих университетских днях (я писал так:-) Однако JSR-133 настолько важен, что требуется чтение для всех программистов на Java. (Удачи)
Начнем с главы 9 "Семантика конечного поля", на странице 25. В частности, прочитайте раздел 9.1.1 "Послестроительная модификация конечных полей". Это имеет смысл разрешить обновление конечных полей. Например, мы могли бы ослабьте требование иметь нечетные поля в JDO.
Если мы внимательно прочитаем раздел 9.1.1, мы увидим, что мы должны только модифицировать окончательные поля как часть нашего процесса строительства. Вариант использования где мы десериализуем объект, а затем, как только мы построили объект, мы инициализируем окончательные поля, прежде чем передавать его. Как только мы сделали объект доступным для другого потока, мы не должны меняться конечные поля с использованием отражения. Результат не был бы предсказуемым.
Он даже говорит об этом: если конечное поле инициализируется во время компиляции константа в объявлении поля, изменения в конечном поле могут не наблюдаются, поскольку использование этого конечного поля заменяется при компиляции время с константой времени компиляции. Это объясняет, почему наше iq-поле остается неизменным, но изменения в стране.
Странно, JDK 5 немного отличается от JDK 1.2.x, поскольку вы не можете изменить статическое конечное поле.
import java.lang.reflect.Field; public class FinalStaticFieldChange { /** Static fields of type String or primitive would get inlined */ private static final String stringValue = "original value"; private static final Object objValue = stringValue; private static void changeStaticField(String name) throws NoSuchFieldException, IllegalAccessException { Field statFinField = FinalStaticFieldChange.class.getDeclaredField(name); statFinField.setAccessible(true); statFinField.set(null, "new Value"); } public static void main(String[] args) throws Exception { changeStaticField("stringValue"); changeStaticField("objValue"); System.out.println("stringValue = " + stringValue); System.out.println("objValue = " + objValue); System.out.println(); } }
Когда мы запускаем это с помощью JDK 1.2.x и JDK 5.x, мы получаем следующее Выход:
java version "1.2.2_014": stringValue = исходное значение objValue = new Значение
java version "1.5.0" Исходный поток "main" IllegalAccessException: Поле является окончательным в файле java.lang.reflect.Field.set(Field.java:656) в FinalStaticFieldChange.changeStaticField(12) at FinalStaticFieldChange.main(16)
Итак, JDK 5 похож на JDK 1.2.x, просто отличается?
Заключение
Знаете ли вы, когда был выпущен JDK 1.3.0? Я изо всех сил пытался выяснить, так что я загрузить и установить его. Файл readme.txt имеет дату 2000/06/02 13:10. Итак, мне больше 4 лет (доброта, это чувствует себя как вчера). JDK 1.3.0 был выпущен за несколько месяцев до того, как я начал писать информационный бюллетень специалистов по Java (tm)! Я думаю, это быть уверенным в том, что очень немногие разработчики Java могут запомнить детали до-JDK1.3.0. Ах, ностальгия не такая, какая она была! Вы не забудьте запустить Java в первый раз и получить эту ошибку: "Не удалось инициализировать потоки: не удается найти класс java/lang/Thread"?