Несмотря на то, что вы скрываете переменную, довольно интересно знать, что вы можете изменить конечные поля в java, так как вы можете прочитать здесь :
Java 5 - "финал" больше не является окончательным
Narve Saetre из Machina Networks в Норвегии прислала мне записку вчера,
упоминая, что было жаль, что мы могли изменить ручку на
окончательный массив. Я не понял его и начал терпеливо объяснять
что мы не можем сделать массив постоянным, и что не было никакого способа
защита содержимого массива. «Нет», сказал он, «мы можем изменить
конечная ручка с использованием отражения. "
Я попробовал пример кода Narve, и невероятно, 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 мы не смогли получить доступ к закрытым полям, используя
отражение. Мы могли бы, однако, создать другого человека с публичным
поля, затем скомпилируйте наш класс против этого и поменяйте местами
классы. Не было проверки доступа во время выполнения, если мы работали
против другого класса к тому, против которого мы собрали.
Однако мы не могли перепривязать окончательные поля во время выполнения, используя либо
обмен или отражение класса.
JavaDocs JDK 1.1.8 для 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 версия "1.3.1_12": поток исключений "main"
IllegalAccessException: поле является окончательным
в java.lang.reflect.Field.set (родной метод)
в FinalFieldChange.change (FinalFieldChange.java:8)
at FinalFieldChange.main (FinalFieldChange.java:12)
java версия "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-версия "1.2.2_014": stringValue = исходное значение objValue = new
Значение
Java-версия "1.5.0" Поток исключений "main" IllegalAccessException:
Поле является окончательным в java.lang.reflect.Field.set (Field.java:656) в
FinalStaticFieldChange.changeStaticField (12) в
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-разработчики могут помнить детали
до JDK 1.3.3. Ааа, ностальгия уже не та, что раньше! Вы
не забудьте запустить Java в первый раз и получить эту ошибку:
"Невозможно инициализировать потоки: не удается найти класс java / lang / Thread"?