Почему String неизменен в Java?

Почему String неизменен в Java?

1. Вступление

В Java строки являются неизменяемыми. Очевидный вопрос, который довольно часто встречается в интервью: «Почему строки конструируются как неизменяемые в Java?»

Джеймс Гослинг, создатель Java,was once asked in an interview, когда следует использовать неизменяемые объекты, на что он отвечает:

Я бы использовал неизменный всякий раз, когда я могу.

Он также поддерживает свой аргумент, утверждая, что функции, которые обеспечивает неизменяемость, такие как кэширование, безопасность, простое повторное использование без репликации и т. Д.

В этом руководстве мы подробнее рассмотрим, почему разработчики языка Java решили сохранить неизменностьString.

2. Что такое неизменный объект?

Неизменяемый объект - этоobject whose internal state remains constant after it has been entirely created. Это означает, что, как только объект был назначен переменной, мы не можем ни обновить ссылку, ни изменить внутреннее состояние каким-либо образом.

У нас есть отдельная статья, в которой подробно обсуждаются неизменные объекты. Для получения дополнительной информации прочтите статьюImmutable Objects in Java.

3. ПочемуString неизменяемый в Java?

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

Давайте обсудим, как все это работает.

3.1. Ввести в пулString

String - это наиболее широко используемая структура данных. Кэширование литераловString и их повторное использование экономит много места в куче, поскольку разные переменныеString ссылаются на один и тот же объект в пулеString. Внутренний пулString служит именно этой цели.

Пул строк Java -the special memory region where Strings are stored by the JVM. ПосколькуStrings неизменяемы в Java, JVM оптимизирует объем выделенной для них памяти, сохраняя только одну копию каждого литералаString в пуле. Этот процесс называется интернированием:

String s1 = "Hello World";
String s2 = "Hello World";

assertThat(s1 == s2).isTrue();

Из-за наличия пулаString в предыдущем примере две разные переменные указывают на один и тот же объектString из пула, тем самым экономя важный ресурс памяти.

image

У нас есть отдельная статья, посвященная пулу JavaString. Для получения дополнительной информацииhead on over to that article.

3.2. Безопасность

String широко используется в приложениях Java для хранения конфиденциальной информации, такой как имена пользователей, пароли, URL-адреса подключений, сетевые подключения и т. Д. Он также широко используется загрузчиками классов JVM при загрузке классов.

Следовательно, защита классаString имеет решающее значение для безопасности всего приложения в целом. Например, рассмотрим этот простой фрагмент кода:

void criticalMethod(String userName) {
    // perform security checks
    if (!isAlphaNumeric(userName)) {
        throw new SecurityException();
    }

    // do some secondary tasks
    initializeDatabase();

    // critical task
    connection.executeUpdate("UPDATE Customers SET Status = 'Active' " +
      " WHERE UserName = '" + userName + "'");
}

В приведенном выше фрагменте кода предположим, что мы получили объектString из ненадежного источника. Сначала мы выполняем все необходимые проверки безопасности, чтобы проверить, является лиString только буквенно-цифровым, а затем выполняем еще несколько операций.

Помните, что у нашего ненадежного исходного метода вызывающего все еще есть ссылка на этот объектuserName.

If Strings were mutable, then by the time we execute the update, we can’t be sure that the String we received, even after performing security checks, would be safe. Ненадежный вызывающий метод все еще имеет ссылку и может изменятьString между проверками целостности. Таким образом, в этом случае наш запрос подвержен SQL-инъекциям. Таким образом, изменяемыйStrings может со временем привести к снижению безопасности.

Также может случиться так, чтоStringuserName виден другому потоку, который затем может изменить свое значение после проверки целостности.

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

3.3. синхронизация

Неизменяемость автоматически делаетString потокобезопасным, поскольку они не изменяются при доступе из нескольких потоков.

Следовательно,immutable objects, in general, can be shared across multiple threads running simultaneously. They’re also thread-safe, потому что, если поток изменяет значение, вместо того, чтобы изменять его, в пулеString будет создан новыйString. Следовательно,Strings безопасны для многопоточности.

3.4. Кэширование хэш-кода

Поскольку объектыString широко используются в качестве структуры данных, они также широко используются в реализациях хеширования, таких какHashMap,HashTable,HashSet и т. Д. При работе с этими реализациями хеширования методhashCode() вызывается довольно часто для сегментирования.

Неизменяемость гарантируетStrings, что их значение не изменится. Итак,the hashCode() method is overridden in String class to facilitate caching, such that the hash is calculated and cached during the first hashCode() call and the same value is returned ever since.

Это, в свою очередь, улучшает производительность коллекций, использующих хеш-реализации при работе с объектамиString.

С другой стороны, изменяемыйStrings будет создавать два разных хэш-кода во время вставки и извлечения, если содержимоеString было изменено после операции, потенциально теряя объект значения вMap.

3.5. Спектакль

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

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

4. Заключение

В этой статье мы можем сделать вывод, чтоStrings are immutable precisely so that their references can be treated as a normal variable and one can pass them around, between methods and across threads, without worrying about whether the actual String object it’s pointing to will change.

Мы также узнали, какие могут быть другие причины, побудившие разработчиков языкаJava сделать этот класс неизменяемым.