Мы не можем быть уверены в том, о чем на самом деле думали Java-дизайнеры при проектировании, String
но мы можем только заключить эти причины, основываясь на преимуществах, которые мы получаем от неизменяемости строк, некоторые из которых
1. Наличие пула констант
Как обсуждалось в статье « Почему хранилище строк в статье String Constant Pool» , каждое приложение создает слишком много строковых объектов, чтобы спасти JVM от первоначального создания большого количества строковых объектов, а затем сбора мусора. JVM хранит все строковые объекты в отдельной области памяти, которая называется пулом констант String, и повторно использует объекты из этого кэшированного пула.
Всякий раз, когда мы создаем строковый литерал, JVM сначала видит, присутствует ли этот литерал в постоянном пуле или нет, и если он там есть, новая ссылка начнет указывать на тот же объект в SCP.
String a = "Naresh";
String b = "Naresh";
String c = "Naresh";
В приведенном выше примере строки объекта со значением Naresh
получит созданный в SCP только один раз , и все ссылки a
, b
, c
будет указывать на тот же объект , но что , если мы попытаемся внести изменения в a
например a.replace("a", "")
.
В идеале, a
должно иметь значение , Nresh
но b
, c
должно оставаться неизменным , поскольку в качестве конечного пользователя мы делаем изменения в a
только. И мы знаем a
, b
, c
все они указывают на тот же объект , так что если мы делаем изменения a
, другие должны также отражать изменения.
Но неизменность строки спасает нас от этого сценария и из-за неизменности строкового объекта строковый объект Naresh
никогда не изменится. Поэтому, когда мы вносим какое-либо изменение a
вместо строкового объекта, Naresh
JVM создает новый объект, назначает его a
и затем вносит изменения в этот объект.
Таким образом, пул String возможен только из-за неизменности String, и если String не был бы неизменным, то кэширование строковых объектов и их повторное использование не имели бы возможности, потому что любая переменная могла бы изменить значение и повредить другие.
И именно поэтому он обрабатывается JVM очень специально и получил специальную область памяти.
2. Поток безопасности
Объект называется поточно-ориентированным, когда на нем работают несколько потоков, но ни один из них не способен испортить его состояние, и объект в любой момент времени поддерживает одно и то же состояние для каждого потока.
Поскольку мы неизменный объект, никто не может быть изменен после его создания, что делает каждый неизменяемый объект по умолчанию безопасным для потоков. Нам не нужно применять какие-либо меры безопасности потоков, такие как создание синхронизированных методов.
Таким образом, из-за своей неизменной природы строковый объект может совместно использоваться несколькими потоками, и даже если им манипулируют многие потоки, он не изменит своего значения.
3. Безопасность
В каждом приложении нам нужно передать несколько секретов, например, имя пользователя \ пароли, URL-адреса подключения и, в общем, всю эту информацию передают как строковый объект.
Теперь предположим, что если бы String не был неизменным по своей природе, то это вызвало бы серьезную угрозу безопасности приложения, поскольку эти значения могут быть изменены, а если это разрешено, то они могут быть изменены из-за неправильно написанного кода или любого другого лица, которое иметь доступ к нашим ссылкам на переменные.
4. Класс загрузки
Как обсуждалось в разделе Создание объектов с помощью Reflection в Java с примером , мы можем использовать Class.forName("class_name")
метод для загрузки класса в память, который снова вызывает для этого другие методы. И даже JVM использует эти методы для загрузки классов.
Но если вы ясно видите, что все эти методы принимают имя класса как строковый объект, поэтому строки загружаются в класс Java, а неизменяемость обеспечивает безопасность, с которой загружается правильный класс ClassLoader
.
Предположим, что если String не был бы неизменным, и мы пытаемся загрузить то, java.lang.Object
что изменяется org.theft.OurObject
между ними, и теперь все наши объекты имеют поведение, которое кто-то может использовать для нежелательных вещей.
5. Кэширование HashCode
Если мы собираемся выполнить какие-либо операции, связанные с хешированием, для любого объекта, мы должны переопределить hashCode()
метод и попытаться сгенерировать точный хэш-код, используя состояние объекта. Если состояние объекта меняется, это означает, что его хеш-код также должен измениться.
Поскольку String является неизменным, поэтому значение, которое содержит один строковый объект, никогда не изменится, что означает, что его хеш-код также не изменится, что дает классу String возможность кэшировать свой хеш-код во время создания объекта.
Да, объект String кэширует свой хэш-код во время создания объекта, что делает его отличным кандидатом для операций, связанных с хешированием, потому что хэш-код не нужно вычислять заново, что экономит нам некоторое время. Вот почему String в основном используется в качестве HashMap
ключей.
Узнайте больше о том, почему String является неизменным и окончательным в Java .