эффект изменения строки с помощью отражения - PullRequest
12 голосов
/ 04 августа 2011

Как мы все знаем, String неизменен в Java.однако, его можно изменить с помощью отражения, получив поле и установив уровень доступа.(Я знаю, что это без консультации, я не собираюсь этого делать, этот вопрос чисто теоретический).

мой вопрос: при условии, что я знаю, что я делаю (и изменяю все поля по мере необходимости), программа будет работать правильно?или jvm делает некоторые оптимизации, которые основаны на неизменяемости String?я буду терпеть потерю производительности?если да, то каково это предположение?что пойдет не так в программе

ps Строка - это всего лишь пример, меня интересует общий ответ, в дополнение к примеру.

спасибо!

Ответы [ 7 ]

5 голосов
/ 02 декабря 2013

После компиляции некоторые строки могут ссылаться на один экземпляр, поэтому вы будете редактировать больше, чем хотите, и никогда не узнаете, что еще вы редактируете.

public static void main(String args[]) throws Exception {
    String s1 = "Hello"; // I want to edit it
    String s2 = "Hello"; // It may be anywhere and must not be edited
    Field f = String.class.getDeclaredField("value");
    f.setAccessible(true);
    f.set(s1, "Doesn't say hello".toCharArray());
    System.out.println(s2);
}

Выход:

Doesn't say hello
2 голосов
/ 04 августа 2011

Вы определенно напрашиваетесь на неприятности, если делаете это.Значит ли это, что вы сразу же увидите ошибки?Нет. Вы можете избежать неприятностей во многих случаях, в зависимости от того, что вы делаете.

Вот несколько случаев, когда это вас укусит:

  • Выизменить строку, которая была объявлена ​​как литерал где-то в коде.Например, у вас есть function, и где-то он называется как function("Bob");в этом сценарии строка "Bob" изменяется во всем приложении (это также будет верно для строки константы , объявленной как final).
  • Вы изменяетестрока, которая используется в операциях подстроки или является результатом операции подстроки.В Java при взятии подстроки строки фактически используется тот же базовый символьный массив, что и у исходной строки, что означает, что изменения в исходной строке будут влиять на подстроки (и наоборот).
  • Вы изменяете строку, которая происходит сбыть использованы в качестве ключа на карте где-нибудь.Он больше не будет сравниваться с исходным значением, поэтому поиск не удастся.

Я знаю, что этот вопрос касается Java, но я написал пост в блоге некоторое время назад, иллюстрируя тольконасколько безумна ваша программа может вести себя, если вы изменяете строку в .NET.Ситуации действительно очень похожи.

2 голосов
/ 04 августа 2011

Что мне приходит в голову, так это интернирование строк - литералы, все, что находится в пуле констант, и все, что вручную intern() ed указывает на один и тот же строковый объект.Если вы начнете возиться с содержимым интернализованного строкового литерала, вы вполне можете увидеть те же самые изменения во всех других литералах, использующих один и тот же базовый объект.

Я не уверен, происходит ли вышеупомянутое на самом деле, так какЯ никогда не пытался (теоретически так и будет, я не знаю, случится ли что-то на сцене, чтобы остановить это, но я сомневаюсь в этом), но такие вещи могут вызвать потенциальные проблемы.Конечно, это может также вызвать проблемы на уровне Java, просто передавая несколько ссылок на одну и ту же строку, а затем используя атаку отражения, чтобы изменить объект из одной из ссылок.Большинство людей (включая меня!) Не будут явно защищаться от подобных вещей в коде, поэтому использование этой атаки с любым кодом, который не является вашим собственным, или вашим собственным кодом, если вы тоже не защитились от этого, может вызвать все видыстранных, ужасных ошибок.

Теоретически это интересная область, но чем больше ты копаешься вокруг, тем больше понимаешь, почему что-то в этом духе - плохая идея!

Если говорить вне строки, естьникаких улучшений производительности, о которых я знаю, для объекта, который является неизменяемым (на самом деле, я не думаю, что JVM может даже сказать в данный момент, является ли объект неизменным, отражая атаки в сторону.) Он может выбросить такие вещи, как checker-framework выключено или все, что пытается статически проанализировать код, чтобы гарантировать его неизменность.

1 голос
/ 04 августа 2011

Я почти уверен, что сама JVM не делает никаких предположений об неизменности строк, поскольку «неизменяемость» в Java не является конструкцией уровня языка;это черта , подразумеваемая реализацией класса, но, как вы заметили, на самом деле не может быть гарантирована при наличии отражения.Таким образом, это также не должно относиться к производительности.

Однако, в значительной степени весь существующий код Java (включая реализацию стандартного API) полагается на неизменность Strings, и если вы нарушите это ожидание,увидеть все виды ошибок.

0 голосов
/ 03 ноября 2014

Чтобы продемонстрировать, как можно испортить программу:

System.out.print("Initial: "); System.out.println(addr);
editIntStr("ADDR_PLACEH", "192.168.1.1");
System.out.print("From var: "); System.out.println(addr);//
System.out.print("Hardcoded: "); System.out.println("ADDR_PLACEH");
System.out.print("Substring: "); System.out.println("ADDR_PLACE" + "H".substring(0));
System.out.print("Equals test: "); System.out.println("ADDR_PLACEH".equals("192.168.1.1"));
System.out.print("Equals test with substring: ");  System.out.println(("ADDR_PLACE" + "H".substring(0)).equals("192.168.1.1"));

Выход:

Initial: ADDR_PLACEH
From var: 192.168.1.1
Hardcoded: 192.168.1.1
Substring: ADDR_PLACEH
Equals test: true
Equals test with substring: false

Результат первого теста Equals странный, не правда ли? Вы не можете ожидать, что ваши коллеги-программисты выяснят, почему Java думает, что они равны ...
Полный код теста: http://pastebin.com/vbstfWX1

0 голосов
/ 02 декабря 2013
public static void main(String args[]){
    String a = "test213";
    String s = new String("test213");
    try {
        System.out.println(s);
        System.out.println(a);
        char[] value = (char[])getFieldValue(s, "value");
        value[1] = 'a';
        System.out.println(s);
        System.out.println(a);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

static Object getFieldValue(String s,String fieldName) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    Object chars = null;
    Field innerCharArray = String.class.getDeclaredField(fieldName);
    innerCharArray.setAccessible(true);
    chars = innerCharArray.get(s);
    return chars;
}

Изменение значения S изменит литерал a, как упоминалось во всех ответах.

0 голосов
/ 04 августа 2011

Закрытыми полями в классе String являются char [], смещение и длина. Изменение любого из них не должно оказывать неблагоприятного воздействия на любой другой объект. Но если вы можете как-то изменить содержимое char [], то вы, вероятно, увидите некоторые неожиданные побочные эффекты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...