Для тех, кто знает эти механизмы здесь не будет ничего нового. Для тех, кто не задумывался о том, как они уживаются вместе - немного уличной магии.
Итак, есть код:
public class SecureClass {
private String msg = "Fail";
public String getMessage() {
return msg;
}
}
public class Main {
public static void main(String[] args) {
SecureClass secureClass = new SecureClass();
System.out.println(secureClass.getMessage());
}
}
После выполнения в консоли по вполне очевидным причинам будет:
>>FailНо мы то хотим выиграть, а не проиграть?
При этом внести исправления в SecureClass не можем или не хотим? Тогда немного уличной магии для класса Main:
public class Main {
public static void main(String[] args) throws Exception {
SecureClass secureClass = new SecureClass();
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("Fail", new char[] {'W', 'i', 'n', '!'});
System.out.println(secureClass.getMessage());
}
}
Для большей наглядности и чтобы не перегружать заметку обработкой всех исключений, которые нужно обрабатывать при использовании reflect, сократил код, написав throws Exception. Запускаем ещё раз и видим:
>>Win!Ну прекрасно, так и должно быть!
Небольшие пояснения:
В локальной безымянной переменной метода main с помощью reflect меняем значение внутреннего поля "value". Таким образом эта локальная переменная уже содержит в себе другую строку (вот вам и невозможность изменить класс String). Эти изменения отражаются и на переменной из класса SecureClass, потому что java старается хранит строки в пуле, когда это возможно и когда программист этому явно не воспрепятствовал. Получается, что в обоих классах строковые переменные "смотрели" на один и тот же объект. Попытаемся намекнуть java, что мы не хотим своими reflect-операциями изменять строки в пуле, указав new String("Fail") вместо "Fail":
public class Main {
public static void main(String[] args) throws Exception {
SecureClass secureClass = new SecureClass();
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(new String("Fail"), new char[] {'W', 'i', 'n', '!'});
System.out.println(secureClass.getMessage());
}
}
Запускаем и видим:
>>FailПотому что теперь переменные указывают на разные объекты. Меняя один - не оказываем никакого влияния на другой.
Если честно, не могу представить ситуации, когда бы стал это использовать, но выглядит забавно.
Комментариев нет:
Отправить комментарий