На самом деле, строка с причинами неизменяемости в Java не имеет большого отношения к безопасности. Две основные причины следующие:
Thead Safety:
Строки - чрезвычайно широко используемый тип объекта. Поэтому более или менее гарантировано использование в многопоточной среде. Строки являются неизменяемыми, чтобы быть уверенными в том, что безопасно разделять строки между потоками. Наличие неизменяемых строк гарантирует, что при передаче строк из потока A в другой поток B поток B не может неожиданно изменить строку потока A.
Это не только помогает упростить и без того довольно сложную задачу многопоточного программирования, но также помогает повысить производительность многопоточных приложений. Доступ к изменяемым объектам должен быть каким-то образом синхронизирован, если к ним можно получить доступ из нескольких потоков, чтобы убедиться, что один поток не пытается прочитать значение вашего объекта, пока он изменяется другим потоком. Правильная синхронизация трудна для программиста и требует больших затрат времени. Неизменяемые объекты не могут быть изменены и поэтому не нуждаются в синхронизации.
Производительность:
Несмотря на то что интернирование String уже упоминалось, оно лишь незначительно увеличивает эффективность памяти для программ Java. Только строковые литералы интернированы. Это означает, что только строки, которые совпадают в вашем исходном коде , будут использовать один и тот же объект String. Если ваша программа динамически создает одинаковые строки, они будут представлены в разных объектах.
Что еще более важно, неизменяемые строки позволяют им обмениваться своими внутренними данными. Для многих строковых операций это означает, что базовый массив символов не нужно копировать. Например, скажем, вы хотите взять пять первых символов строки. В Java вы бы вызвали myString.substring (0,5). В этом случае метод substring () просто создает новый объект String, который разделяет базовый тип char [] myString, но кто знает, что он начинается с индекса 0 и заканчивается индексом 5 этого char []. Чтобы поместить это в графическую форму, вы должны получить следующее:
| myString |
v v
"The quick brown fox jumps over the lazy dog" <-- shared char[]
^ ^
| | myString.substring(0,5)
Это делает такие операции чрезвычайно дешевыми, и O (1), поскольку операция не зависит ни от длины исходной строки, ни от длины подстроки, которую мы должны извлечь. Такое поведение также имеет некоторые преимущества памяти, так как многие строки могут совместно использовать свой основной символ [].