Для тех, кто знает эти механизмы здесь не будет ничего нового. Для тех, кто не задумывался о том, как они уживаются вместе - немного уличной магии.
Итак, есть код:
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Потому что теперь переменные указывают на разные объекты. Меняя один - не оказываем никакого влияния на другой.
Если честно, не могу представить ситуации, когда бы стал это использовать, но выглядит забавно.
Комментариев нет:
Отправить комментарий